mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 02:32:09 -04:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
1) Seccomp BPF filters can now be JIT'd, from Alexei Starovoitov.
2) Multiqueue support in xen-netback and xen-netfront, from Andrew J
Benniston.
3) Allow tweaking of aggregation settings in cdc_ncm driver, from Bjørn
Mork.
4) BPF now has a "random" opcode, from Chema Gonzalez.
5) Add more BPF documentation and improve test framework, from Daniel
Borkmann.
6) Support TCP fastopen over ipv6, from Daniel Lee.
7) Add software TSO helper functions and use them to support software
TSO in mvneta and mv643xx_eth drivers. From Ezequiel Garcia.
8) Support software TSO in fec driver too, from Nimrod Andy.
9) Add Broadcom SYSTEMPORT driver, from Florian Fainelli.
10) Handle broadcasts more gracefully over macvlan when there are large
numbers of interfaces configured, from Herbert Xu.
11) Allow more control over fwmark used for non-socket based responses,
from Lorenzo Colitti.
12) Do TCP congestion window limiting based upon measurements, from Neal
Cardwell.
13) Support busy polling in SCTP, from Neal Horman.
14) Allow RSS key to be configured via ethtool, from Venkata Duvvuru.
15) Bridge promisc mode handling improvements from Vlad Yasevich.
16) Don't use inetpeer entries to implement ID generation any more, it
performs poorly, from Eric Dumazet.
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1522 commits)
rtnetlink: fix userspace API breakage for iproute2 < v3.9.0
tcp: fixing TLP's FIN recovery
net: fec: Add software TSO support
net: fec: Add Scatter/gather support
net: fec: Increase buffer descriptor entry number
net: fec: Factorize feature setting
net: fec: Enable IP header hardware checksum
net: fec: Factorize the .xmit transmit function
bridge: fix compile error when compiling without IPv6 support
bridge: fix smatch warning / potential null pointer dereference
via-rhine: fix full-duplex with autoneg disable
bnx2x: Enlarge the dorq threshold for VFs
bnx2x: Check for UNDI in uncommon branch
bnx2x: Fix 1G-baseT link
bnx2x: Fix link for KR with swapped polarity lane
sctp: Fix sk_ack_backlog wrap-around problem
net/core: Add VF link state control policy
net/fsl: xgmac_mdio is dependent on OF_MDIO
net/fsl: Make xgmac_mdio read error message useful
net_sched: drr: warn when qdisc is not work conserving
...
This commit is contained in:
commit
f9da455b93
1265 changed files with 61466 additions and 22881 deletions
|
|
@ -157,7 +157,7 @@ static inline struct aggregator *__get_first_agg(struct port *port)
|
|||
|
||||
rcu_read_lock();
|
||||
first_slave = bond_first_slave_rcu(bond);
|
||||
agg = first_slave ? &(SLAVE_AD_INFO(first_slave).aggregator) : NULL;
|
||||
agg = first_slave ? &(SLAVE_AD_INFO(first_slave)->aggregator) : NULL;
|
||||
rcu_read_unlock();
|
||||
|
||||
return agg;
|
||||
|
|
@ -192,7 +192,7 @@ static inline void __enable_port(struct port *port)
|
|||
{
|
||||
struct slave *slave = port->slave;
|
||||
|
||||
if ((slave->link == BOND_LINK_UP) && IS_UP(slave->dev))
|
||||
if ((slave->link == BOND_LINK_UP) && bond_slave_is_up(slave))
|
||||
bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_LATER);
|
||||
}
|
||||
|
||||
|
|
@ -241,7 +241,7 @@ static inline int __check_agg_selection_timer(struct port *port)
|
|||
*/
|
||||
static inline void __get_state_machine_lock(struct port *port)
|
||||
{
|
||||
spin_lock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock));
|
||||
spin_lock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -250,7 +250,7 @@ static inline void __get_state_machine_lock(struct port *port)
|
|||
*/
|
||||
static inline void __release_state_machine_lock(struct port *port)
|
||||
{
|
||||
spin_unlock_bh(&(SLAVE_AD_INFO(port->slave).state_machine_lock));
|
||||
spin_unlock_bh(&(SLAVE_AD_INFO(port->slave)->state_machine_lock));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -350,7 +350,7 @@ static u8 __get_duplex(struct port *port)
|
|||
static inline void __initialize_port_locks(struct slave *slave)
|
||||
{
|
||||
/* make sure it isn't called twice */
|
||||
spin_lock_init(&(SLAVE_AD_INFO(slave).state_machine_lock));
|
||||
spin_lock_init(&(SLAVE_AD_INFO(slave)->state_machine_lock));
|
||||
}
|
||||
|
||||
/* Conversions */
|
||||
|
|
@ -688,8 +688,8 @@ static struct aggregator *__get_active_agg(struct aggregator *aggregator)
|
|||
struct slave *slave;
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter)
|
||||
if (SLAVE_AD_INFO(slave).aggregator.is_active)
|
||||
return &(SLAVE_AD_INFO(slave).aggregator);
|
||||
if (SLAVE_AD_INFO(slave)->aggregator.is_active)
|
||||
return &(SLAVE_AD_INFO(slave)->aggregator);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -1293,7 +1293,7 @@ static void ad_port_selection_logic(struct port *port)
|
|||
}
|
||||
/* search on all aggregators for a suitable aggregator for this port */
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
aggregator = &(SLAVE_AD_INFO(slave).aggregator);
|
||||
aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
|
||||
|
||||
/* keep a free aggregator for later use(if needed) */
|
||||
if (!aggregator->lag_ports) {
|
||||
|
|
@ -1504,7 +1504,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
|
|||
best = (active && agg_device_up(active)) ? active : NULL;
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
agg = &(SLAVE_AD_INFO(slave).aggregator);
|
||||
agg = &(SLAVE_AD_INFO(slave)->aggregator);
|
||||
|
||||
agg->is_active = 0;
|
||||
|
||||
|
|
@ -1549,7 +1549,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
|
|||
best->slave ? best->slave->dev->name : "NULL");
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
agg = &(SLAVE_AD_INFO(slave).aggregator);
|
||||
agg = &(SLAVE_AD_INFO(slave)->aggregator);
|
||||
|
||||
pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n",
|
||||
agg->aggregator_identifier, agg->num_of_ports,
|
||||
|
|
@ -1840,16 +1840,16 @@ void bond_3ad_bind_slave(struct slave *slave)
|
|||
struct aggregator *aggregator;
|
||||
|
||||
/* check that the slave has not been initialized yet. */
|
||||
if (SLAVE_AD_INFO(slave).port.slave != slave) {
|
||||
if (SLAVE_AD_INFO(slave)->port.slave != slave) {
|
||||
|
||||
/* port initialization */
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
|
||||
ad_initialize_port(port, bond->params.lacp_fast);
|
||||
|
||||
__initialize_port_locks(slave);
|
||||
port->slave = slave;
|
||||
port->actor_port_number = SLAVE_AD_INFO(slave).id;
|
||||
port->actor_port_number = SLAVE_AD_INFO(slave)->id;
|
||||
/* key is determined according to the link speed, duplex and user key(which
|
||||
* is yet not supported)
|
||||
*/
|
||||
|
|
@ -1874,7 +1874,7 @@ void bond_3ad_bind_slave(struct slave *slave)
|
|||
__disable_port(port);
|
||||
|
||||
/* aggregator initialization */
|
||||
aggregator = &(SLAVE_AD_INFO(slave).aggregator);
|
||||
aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
|
||||
|
||||
ad_initialize_agg(aggregator);
|
||||
|
||||
|
|
@ -1903,8 +1903,8 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
struct slave *slave_iter;
|
||||
struct list_head *iter;
|
||||
|
||||
aggregator = &(SLAVE_AD_INFO(slave).aggregator);
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
aggregator = &(SLAVE_AD_INFO(slave)->aggregator);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
|
||||
/* if slave is null, the whole port is not initialized */
|
||||
if (!port->slave) {
|
||||
|
|
@ -1932,7 +1932,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
(aggregator->lag_ports->next_port_in_aggregator)) {
|
||||
/* find new aggregator for the related port(s) */
|
||||
bond_for_each_slave(bond, slave_iter, iter) {
|
||||
new_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
|
||||
new_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
|
||||
/* if the new aggregator is empty, or it is
|
||||
* connected to our port only
|
||||
*/
|
||||
|
|
@ -2010,7 +2010,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
|
|||
|
||||
/* find the aggregator that this port is connected to */
|
||||
bond_for_each_slave(bond, slave_iter, iter) {
|
||||
temp_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator);
|
||||
temp_aggregator = &(SLAVE_AD_INFO(slave_iter)->aggregator);
|
||||
prev_port = NULL;
|
||||
/* search the port in the aggregator's related ports */
|
||||
for (temp_port = temp_aggregator->lag_ports; temp_port;
|
||||
|
|
@ -2076,7 +2076,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
|
|||
if (BOND_AD_INFO(bond).agg_select_timer &&
|
||||
!(--BOND_AD_INFO(bond).agg_select_timer)) {
|
||||
slave = bond_first_slave_rcu(bond);
|
||||
port = slave ? &(SLAVE_AD_INFO(slave).port) : NULL;
|
||||
port = slave ? &(SLAVE_AD_INFO(slave)->port) : NULL;
|
||||
|
||||
/* select the active aggregator for the bond */
|
||||
if (port) {
|
||||
|
|
@ -2094,7 +2094,7 @@ void bond_3ad_state_machine_handler(struct work_struct *work)
|
|||
|
||||
/* for each port run the state machines */
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
if (!port->slave) {
|
||||
pr_warn_ratelimited("%s: Warning: Found an uninitialized port\n",
|
||||
bond->dev->name);
|
||||
|
|
@ -2155,7 +2155,7 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave,
|
|||
|
||||
if (length >= sizeof(struct lacpdu)) {
|
||||
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
|
||||
if (!port->slave) {
|
||||
pr_warn_ratelimited("%s: Warning: port of slave %s is uninitialized\n",
|
||||
|
|
@ -2212,7 +2212,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
|
|||
{
|
||||
struct port *port;
|
||||
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
|
||||
/* if slave is null, the whole port is not initialized */
|
||||
if (!port->slave) {
|
||||
|
|
@ -2245,7 +2245,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
|
|||
{
|
||||
struct port *port;
|
||||
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
|
||||
/* if slave is null, the whole port is not initialized */
|
||||
if (!port->slave) {
|
||||
|
|
@ -2279,7 +2279,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
|
|||
{
|
||||
struct port *port;
|
||||
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
|
||||
/* if slave is null, the whole port is not initialized */
|
||||
if (!port->slave) {
|
||||
|
|
@ -2347,7 +2347,7 @@ int bond_3ad_set_carrier(struct bonding *bond)
|
|||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
active = __get_active_agg(&(SLAVE_AD_INFO(first_slave).aggregator));
|
||||
active = __get_active_agg(&(SLAVE_AD_INFO(first_slave)->aggregator));
|
||||
if (active) {
|
||||
/* are enough slaves available to consider link up? */
|
||||
if (active->num_of_ports < bond->params.min_links) {
|
||||
|
|
@ -2384,7 +2384,7 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond,
|
|||
struct port *port;
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
if (port->aggregator && port->aggregator->is_active) {
|
||||
aggregator = port->aggregator;
|
||||
break;
|
||||
|
|
@ -2440,22 +2440,22 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev)
|
|||
goto err_free;
|
||||
}
|
||||
|
||||
slave_agg_no = bond_xmit_hash(bond, skb, slaves_in_agg);
|
||||
slave_agg_no = bond_xmit_hash(bond, skb) % slaves_in_agg;
|
||||
first_ok_slave = NULL;
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
agg = SLAVE_AD_INFO(slave).port.aggregator;
|
||||
agg = SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
if (!agg || agg->aggregator_identifier != agg_id)
|
||||
continue;
|
||||
|
||||
if (slave_agg_no >= 0) {
|
||||
if (!first_ok_slave && SLAVE_IS_OK(slave))
|
||||
if (!first_ok_slave && bond_slave_can_tx(slave))
|
||||
first_ok_slave = slave;
|
||||
slave_agg_no--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SLAVE_IS_OK(slave)) {
|
||||
if (bond_slave_can_tx(slave)) {
|
||||
bond_dev_queue_xmit(bond, skb, slave->dev);
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -2522,7 +2522,7 @@ void bond_3ad_update_lacp_rate(struct bonding *bond)
|
|||
|
||||
lacp_fast = bond->params.lacp_fast;
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
port = &(SLAVE_AD_INFO(slave).port);
|
||||
port = &(SLAVE_AD_INFO(slave)->port);
|
||||
__get_state_machine_lock(port);
|
||||
if (lacp_fast)
|
||||
port->actor_oper_port_state |= AD_STATE_LACP_TIMEOUT;
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ static struct slave *tlb_get_least_loaded_slave(struct bonding *bond)
|
|||
|
||||
/* Find the slave with the largest gap */
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (SLAVE_IS_OK(slave)) {
|
||||
if (bond_slave_can_tx(slave)) {
|
||||
long long gap = compute_gap(slave);
|
||||
|
||||
if (max_gap < gap) {
|
||||
|
|
@ -384,7 +384,7 @@ static struct slave *rlb_next_rx_slave(struct bonding *bond)
|
|||
bool found = false;
|
||||
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
if (!SLAVE_IS_OK(slave))
|
||||
if (!bond_slave_can_tx(slave))
|
||||
continue;
|
||||
if (!found) {
|
||||
if (!before || before->speed < slave->speed)
|
||||
|
|
@ -417,7 +417,7 @@ static struct slave *__rlb_next_rx_slave(struct bonding *bond)
|
|||
bool found = false;
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (!SLAVE_IS_OK(slave))
|
||||
if (!bond_slave_can_tx(slave))
|
||||
continue;
|
||||
if (!found) {
|
||||
if (!before || before->speed < slave->speed)
|
||||
|
|
@ -755,7 +755,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond)
|
|||
/* Don't modify or load balance ARPs that do not originate locally
|
||||
* (e.g.,arrive via a bridge).
|
||||
*/
|
||||
if (!bond_slave_has_mac_rcu(bond, arp->mac_src))
|
||||
if (!bond_slave_has_mac_rx(bond, arp->mac_src))
|
||||
return NULL;
|
||||
|
||||
if (arp->op_code == htons(ARPOP_REPLY)) {
|
||||
|
|
@ -1039,11 +1039,14 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
|
|||
struct bonding *bond = bond_get_bond_by_slave(slave);
|
||||
struct net_device *upper;
|
||||
struct list_head *iter;
|
||||
struct bond_vlan_tag tags[BOND_MAX_VLAN_ENCAP];
|
||||
|
||||
/* send untagged */
|
||||
alb_send_lp_vid(slave, mac_addr, 0, 0);
|
||||
|
||||
/* loop through vlans and send one packet for each */
|
||||
/* loop through all devices and see if we need to send a packet
|
||||
* for that device.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) {
|
||||
if (is_vlan_dev(upper) && vlan_get_encap_level(upper) == 0) {
|
||||
|
|
@ -1059,6 +1062,16 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[],
|
|||
vlan_dev_vlan_id(upper));
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is a macvlan device, then only send updates
|
||||
* when strict_match is turned off.
|
||||
*/
|
||||
if (netif_is_macvlan(upper) && !strict_match) {
|
||||
memset(tags, 0, sizeof(tags));
|
||||
bond_verify_device_path(bond->dev, upper, tags);
|
||||
alb_send_lp_vid(slave, upper->dev_addr,
|
||||
tags[0].vlan_proto, tags[0].vlan_id);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
|
@ -1068,7 +1081,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
|
|||
struct net_device *dev = slave->dev;
|
||||
struct sockaddr s_addr;
|
||||
|
||||
if (slave->bond->params.mode == BOND_MODE_TLB) {
|
||||
if (BOND_MODE(slave->bond) == BOND_MODE_TLB) {
|
||||
memcpy(dev->dev_addr, addr, dev->addr_len);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1111,13 +1124,13 @@ static void alb_swap_mac_addr(struct slave *slave1, struct slave *slave2)
|
|||
static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
|
||||
struct slave *slave2)
|
||||
{
|
||||
int slaves_state_differ = (SLAVE_IS_OK(slave1) != SLAVE_IS_OK(slave2));
|
||||
int slaves_state_differ = (bond_slave_can_tx(slave1) != bond_slave_can_tx(slave2));
|
||||
struct slave *disabled_slave = NULL;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* fasten the change in the switch */
|
||||
if (SLAVE_IS_OK(slave1)) {
|
||||
if (bond_slave_can_tx(slave1)) {
|
||||
alb_send_learning_packets(slave1, slave1->dev->dev_addr, false);
|
||||
if (bond->alb_info.rlb_enabled) {
|
||||
/* inform the clients that the mac address
|
||||
|
|
@ -1129,7 +1142,7 @@ static void alb_fasten_mac_swap(struct bonding *bond, struct slave *slave1,
|
|||
disabled_slave = slave1;
|
||||
}
|
||||
|
||||
if (SLAVE_IS_OK(slave2)) {
|
||||
if (bond_slave_can_tx(slave2)) {
|
||||
alb_send_learning_packets(slave2, slave2->dev->dev_addr, false);
|
||||
if (bond->alb_info.rlb_enabled) {
|
||||
/* inform the clients that the mac address
|
||||
|
|
@ -1358,6 +1371,77 @@ void bond_alb_deinitialize(struct bonding *bond)
|
|||
rlb_deinitialize(bond);
|
||||
}
|
||||
|
||||
static int bond_do_alb_xmit(struct sk_buff *skb, struct bonding *bond,
|
||||
struct slave *tx_slave)
|
||||
{
|
||||
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
|
||||
struct ethhdr *eth_data = eth_hdr(skb);
|
||||
|
||||
if (!tx_slave) {
|
||||
/* unbalanced or unassigned, send through primary */
|
||||
tx_slave = rcu_dereference(bond->curr_active_slave);
|
||||
if (bond->params.tlb_dynamic_lb)
|
||||
bond_info->unbalanced_load += skb->len;
|
||||
}
|
||||
|
||||
if (tx_slave && bond_slave_can_tx(tx_slave)) {
|
||||
if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
|
||||
ether_addr_copy(eth_data->h_source,
|
||||
tx_slave->dev->dev_addr);
|
||||
}
|
||||
|
||||
bond_dev_queue_xmit(bond, skb, tx_slave->dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (tx_slave && bond->params.tlb_dynamic_lb) {
|
||||
_lock_tx_hashtbl(bond);
|
||||
__tlb_clear_slave(bond, tx_slave, 0);
|
||||
_unlock_tx_hashtbl(bond);
|
||||
}
|
||||
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb_any(skb);
|
||||
out:
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
struct ethhdr *eth_data;
|
||||
struct slave *tx_slave = NULL;
|
||||
u32 hash_index;
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
eth_data = eth_hdr(skb);
|
||||
|
||||
/* Do not TX balance any multicast or broadcast */
|
||||
if (!is_multicast_ether_addr(eth_data->h_dest)) {
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
case htons(ETH_P_IPX):
|
||||
/* In case of IPX, it will falback to L2 hash */
|
||||
case htons(ETH_P_IPV6):
|
||||
hash_index = bond_xmit_hash(bond, skb);
|
||||
if (bond->params.tlb_dynamic_lb) {
|
||||
tx_slave = tlb_choose_channel(bond,
|
||||
hash_index & 0xFF,
|
||||
skb->len);
|
||||
} else {
|
||||
struct list_head *iter;
|
||||
int idx = hash_index % bond->slave_cnt;
|
||||
|
||||
bond_for_each_slave_rcu(bond, tx_slave, iter)
|
||||
if (--idx < 0)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bond_do_alb_xmit(skb, bond, tx_slave);
|
||||
}
|
||||
|
||||
int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
||||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
|
|
@ -1366,7 +1450,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
struct slave *tx_slave = NULL;
|
||||
static const __be32 ip_bcast = htonl(0xffffffff);
|
||||
int hash_size = 0;
|
||||
int do_tx_balance = 1;
|
||||
bool do_tx_balance = true;
|
||||
u32 hash_index = 0;
|
||||
const u8 *hash_start = NULL;
|
||||
struct ipv6hdr *ip6hdr;
|
||||
|
|
@ -1381,7 +1465,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast) ||
|
||||
(iph->daddr == ip_bcast) ||
|
||||
(iph->protocol == IPPROTO_IGMP)) {
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
hash_start = (char *)&(iph->daddr);
|
||||
|
|
@ -1393,7 +1477,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
* that here just in case.
|
||||
*/
|
||||
if (ether_addr_equal_64bits(eth_data->h_dest, mac_bcast)) {
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1401,7 +1485,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
* broadcasts in IPv4.
|
||||
*/
|
||||
if (ether_addr_equal_64bits(eth_data->h_dest, mac_v6_allmcast)) {
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1411,7 +1495,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
*/
|
||||
ip6hdr = ipv6_hdr(skb);
|
||||
if (ipv6_addr_any(&ip6hdr->saddr)) {
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1421,7 +1505,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
case ETH_P_IPX:
|
||||
if (ipx_hdr(skb)->ipx_checksum != IPX_NO_CHECKSUM) {
|
||||
/* something is wrong with this packet */
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1430,7 +1514,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
* this family since it has an "ARP" like
|
||||
* mechanism
|
||||
*/
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1438,12 +1522,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
hash_size = ETH_ALEN;
|
||||
break;
|
||||
case ETH_P_ARP:
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
if (bond_info->rlb_enabled)
|
||||
tx_slave = rlb_arp_xmit(skb, bond);
|
||||
break;
|
||||
default:
|
||||
do_tx_balance = 0;
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1452,32 +1536,7 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
tx_slave = tlb_choose_channel(bond, hash_index, skb->len);
|
||||
}
|
||||
|
||||
if (!tx_slave) {
|
||||
/* unbalanced or unassigned, send through primary */
|
||||
tx_slave = rcu_dereference(bond->curr_active_slave);
|
||||
bond_info->unbalanced_load += skb->len;
|
||||
}
|
||||
|
||||
if (tx_slave && SLAVE_IS_OK(tx_slave)) {
|
||||
if (tx_slave != rcu_dereference(bond->curr_active_slave)) {
|
||||
ether_addr_copy(eth_data->h_source,
|
||||
tx_slave->dev->dev_addr);
|
||||
}
|
||||
|
||||
bond_dev_queue_xmit(bond, skb, tx_slave->dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (tx_slave) {
|
||||
_lock_tx_hashtbl(bond);
|
||||
__tlb_clear_slave(bond, tx_slave, 0);
|
||||
_unlock_tx_hashtbl(bond);
|
||||
}
|
||||
|
||||
/* no suitable interface, frame not sent */
|
||||
dev_kfree_skb_any(skb);
|
||||
out:
|
||||
return NETDEV_TX_OK;
|
||||
return bond_do_alb_xmit(skb, bond, tx_slave);
|
||||
}
|
||||
|
||||
void bond_alb_monitor(struct work_struct *work)
|
||||
|
|
@ -1514,8 +1573,10 @@ void bond_alb_monitor(struct work_struct *work)
|
|||
/* If updating current_active, use all currently
|
||||
* user mac addreses (!strict_match). Otherwise, only
|
||||
* use mac of the slave device.
|
||||
* In RLB mode, we always use strict matches.
|
||||
*/
|
||||
strict_match = (slave != bond->curr_active_slave);
|
||||
strict_match = (slave != bond->curr_active_slave ||
|
||||
bond_info->rlb_enabled);
|
||||
alb_send_learning_packets(slave, slave->dev->dev_addr,
|
||||
strict_match);
|
||||
}
|
||||
|
|
@ -1719,7 +1780,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave
|
|||
/* in TLB mode, the slave might flip down/up with the old dev_addr,
|
||||
* and thus filter bond->dev_addr's packets, so force bond's mac
|
||||
*/
|
||||
if (bond->params.mode == BOND_MODE_TLB) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_TLB) {
|
||||
struct sockaddr sa;
|
||||
u8 tmp_addr[ETH_ALEN];
|
||||
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave);
|
|||
void bond_alb_handle_link_change(struct bonding *bond, struct slave *slave, char link);
|
||||
void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave);
|
||||
int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev);
|
||||
int bond_tlb_xmit(struct sk_buff *skb, struct net_device *bond_dev);
|
||||
void bond_alb_monitor(struct work_struct *);
|
||||
int bond_alb_set_mac_address(struct net_device *bond_dev, void *addr);
|
||||
void bond_alb_clear_vlan(struct bonding *bond, unsigned short vlan_id);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v)
|
|||
struct rlb_client_info *client_info;
|
||||
u32 hash_index;
|
||||
|
||||
if (bond->params.mode != BOND_MODE_ALB)
|
||||
if (BOND_MODE(bond) != BOND_MODE_ALB)
|
||||
return 0;
|
||||
|
||||
seq_printf(m, "SourceIP DestinationIP "
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ static int bond_set_carrier(struct bonding *bond)
|
|||
if (!bond_has_slaves(bond))
|
||||
goto down;
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD)
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
return bond_3ad_set_carrier(bond);
|
||||
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
|
|
@ -497,7 +497,7 @@ static int bond_set_promiscuity(struct bonding *bond, int inc)
|
|||
struct list_head *iter;
|
||||
int err = 0;
|
||||
|
||||
if (USES_PRIMARY(bond->params.mode)) {
|
||||
if (bond_uses_primary(bond)) {
|
||||
/* write lock already acquired */
|
||||
if (bond->curr_active_slave) {
|
||||
err = dev_set_promiscuity(bond->curr_active_slave->dev,
|
||||
|
|
@ -523,7 +523,7 @@ static int bond_set_allmulti(struct bonding *bond, int inc)
|
|||
struct list_head *iter;
|
||||
int err = 0;
|
||||
|
||||
if (USES_PRIMARY(bond->params.mode)) {
|
||||
if (bond_uses_primary(bond)) {
|
||||
/* write lock already acquired */
|
||||
if (bond->curr_active_slave) {
|
||||
err = dev_set_allmulti(bond->curr_active_slave->dev,
|
||||
|
|
@ -574,7 +574,7 @@ static void bond_hw_addr_flush(struct net_device *bond_dev,
|
|||
dev_uc_unsync(slave_dev, bond_dev);
|
||||
dev_mc_unsync(slave_dev, bond_dev);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
/* del lacpdu mc addr from mc list */
|
||||
u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
|
||||
|
||||
|
|
@ -585,8 +585,8 @@ static void bond_hw_addr_flush(struct net_device *bond_dev,
|
|||
/*--------------------------- Active slave change ---------------------------*/
|
||||
|
||||
/* Update the hardware address list and promisc/allmulti for the new and
|
||||
* old active slaves (if any). Modes that are !USES_PRIMARY keep all
|
||||
* slaves up date at all times; only the USES_PRIMARY modes need to call
|
||||
* old active slaves (if any). Modes that are not using primary keep all
|
||||
* slaves up date at all times; only the modes that use primary need to call
|
||||
* this function to swap these settings during a failover.
|
||||
*/
|
||||
static void bond_hw_addr_swap(struct bonding *bond, struct slave *new_active,
|
||||
|
|
@ -747,7 +747,7 @@ static struct slave *bond_find_best_slave(struct bonding *bond)
|
|||
bond_for_each_slave(bond, slave, iter) {
|
||||
if (slave->link == BOND_LINK_UP)
|
||||
return slave;
|
||||
if (slave->link == BOND_LINK_BACK && IS_UP(slave->dev) &&
|
||||
if (slave->link == BOND_LINK_BACK && bond_slave_is_up(slave) &&
|
||||
slave->delay < mintime) {
|
||||
mintime = slave->delay;
|
||||
bestslave = slave;
|
||||
|
|
@ -801,7 +801,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
|
|||
new_active->last_link_up = jiffies;
|
||||
|
||||
if (new_active->link == BOND_LINK_BACK) {
|
||||
if (USES_PRIMARY(bond->params.mode)) {
|
||||
if (bond_uses_primary(bond)) {
|
||||
pr_info("%s: making interface %s the new active one %d ms earlier\n",
|
||||
bond->dev->name, new_active->dev->name,
|
||||
(bond->params.updelay - new_active->delay) * bond->params.miimon);
|
||||
|
|
@ -810,20 +810,20 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
|
|||
new_active->delay = 0;
|
||||
new_active->link = BOND_LINK_UP;
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD)
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_3ad_handle_link_change(new_active, BOND_LINK_UP);
|
||||
|
||||
if (bond_is_lb(bond))
|
||||
bond_alb_handle_link_change(bond, new_active, BOND_LINK_UP);
|
||||
} else {
|
||||
if (USES_PRIMARY(bond->params.mode)) {
|
||||
if (bond_uses_primary(bond)) {
|
||||
pr_info("%s: making interface %s the new active one\n",
|
||||
bond->dev->name, new_active->dev->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (USES_PRIMARY(bond->params.mode))
|
||||
if (bond_uses_primary(bond))
|
||||
bond_hw_addr_swap(bond, new_active, old_active);
|
||||
|
||||
if (bond_is_lb(bond)) {
|
||||
|
|
@ -838,7 +838,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
|
|||
rcu_assign_pointer(bond->curr_active_slave, new_active);
|
||||
}
|
||||
|
||||
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) {
|
||||
if (old_active)
|
||||
bond_set_slave_inactive_flags(old_active,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
|
@ -876,8 +876,8 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
|
|||
* resend only if bond is brought up with the affected
|
||||
* bonding modes and the retransmission is enabled */
|
||||
if (netif_running(bond->dev) && (bond->params.resend_igmp > 0) &&
|
||||
((USES_PRIMARY(bond->params.mode) && new_active) ||
|
||||
bond->params.mode == BOND_MODE_ROUNDROBIN)) {
|
||||
((bond_uses_primary(bond) && new_active) ||
|
||||
BOND_MODE(bond) == BOND_MODE_ROUNDROBIN)) {
|
||||
bond->igmp_retrans = bond->params.resend_igmp;
|
||||
queue_delayed_work(bond->wq, &bond->mcast_work, 1);
|
||||
}
|
||||
|
|
@ -958,7 +958,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
|
|||
struct slave *slave;
|
||||
|
||||
bond_for_each_slave(bond, slave, iter)
|
||||
if (IS_UP(slave->dev))
|
||||
if (bond_slave_is_up(slave))
|
||||
slave_disable_netpoll(slave);
|
||||
}
|
||||
|
||||
|
|
@ -1038,6 +1038,7 @@ static void bond_compute_features(struct bonding *bond)
|
|||
|
||||
if (!bond_has_slaves(bond))
|
||||
goto done;
|
||||
vlan_features &= NETIF_F_ALL_FOR_ALL;
|
||||
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
vlan_features = netdev_increment_features(vlan_features,
|
||||
|
|
@ -1084,7 +1085,7 @@ static bool bond_should_deliver_exact_match(struct sk_buff *skb,
|
|||
struct bonding *bond)
|
||||
{
|
||||
if (bond_is_slave_inactive(slave)) {
|
||||
if (bond->params.mode == BOND_MODE_ALB &&
|
||||
if (BOND_MODE(bond) == BOND_MODE_ALB &&
|
||||
skb->pkt_type != PACKET_BROADCAST &&
|
||||
skb->pkt_type != PACKET_MULTICAST)
|
||||
return false;
|
||||
|
|
@ -1126,7 +1127,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
|
|||
|
||||
skb->dev = bond->dev;
|
||||
|
||||
if (bond->params.mode == BOND_MODE_ALB &&
|
||||
if (BOND_MODE(bond) == BOND_MODE_ALB &&
|
||||
bond->dev->priv_flags & IFF_BRIDGE_PORT &&
|
||||
skb->pkt_type == PACKET_HOST) {
|
||||
|
||||
|
|
@ -1163,6 +1164,35 @@ static void bond_upper_dev_unlink(struct net_device *bond_dev,
|
|||
rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static struct slave *bond_alloc_slave(struct bonding *bond)
|
||||
{
|
||||
struct slave *slave = NULL;
|
||||
|
||||
slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
|
||||
if (!slave)
|
||||
return NULL;
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
SLAVE_AD_INFO(slave) = kzalloc(sizeof(struct ad_slave_info),
|
||||
GFP_KERNEL);
|
||||
if (!SLAVE_AD_INFO(slave)) {
|
||||
kfree(slave);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return slave;
|
||||
}
|
||||
|
||||
static void bond_free_slave(struct slave *slave)
|
||||
{
|
||||
struct bonding *bond = bond_get_bond_by_slave(slave);
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
kfree(SLAVE_AD_INFO(slave));
|
||||
|
||||
kfree(slave);
|
||||
}
|
||||
|
||||
/* enslave device <slave> to bond device <master> */
|
||||
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
||||
{
|
||||
|
|
@ -1269,7 +1299,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
if (!bond_has_slaves(bond)) {
|
||||
pr_warn("%s: Warning: The first slave device specified does not support setting the MAC address\n",
|
||||
bond_dev->name);
|
||||
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP) {
|
||||
bond->params.fail_over_mac = BOND_FOM_ACTIVE;
|
||||
pr_warn("%s: Setting fail_over_mac to active for active-backup mode\n",
|
||||
bond_dev->name);
|
||||
|
|
@ -1290,11 +1320,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
bond->dev->addr_assign_type == NET_ADDR_RANDOM)
|
||||
bond_set_dev_addr(bond->dev, slave_dev);
|
||||
|
||||
new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL);
|
||||
new_slave = bond_alloc_slave(bond);
|
||||
if (!new_slave) {
|
||||
res = -ENOMEM;
|
||||
goto err_undo_flags;
|
||||
}
|
||||
|
||||
new_slave->bond = bond;
|
||||
new_slave->dev = slave_dev;
|
||||
/*
|
||||
* Set the new_slave's queue_id to be zero. Queue ID mapping
|
||||
* is set via sysfs or module option if desired.
|
||||
|
|
@ -1317,7 +1350,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
ether_addr_copy(new_slave->perm_hwaddr, slave_dev->dev_addr);
|
||||
|
||||
if (!bond->params.fail_over_mac ||
|
||||
bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
|
||||
BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
|
||||
/*
|
||||
* Set slave to master's mac address. The application already
|
||||
* set the master's mac address to that of the first slave
|
||||
|
|
@ -1338,8 +1371,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
goto err_restore_mac;
|
||||
}
|
||||
|
||||
new_slave->bond = bond;
|
||||
new_slave->dev = slave_dev;
|
||||
slave_dev->priv_flags |= IFF_BONDING;
|
||||
|
||||
if (bond_is_lb(bond)) {
|
||||
|
|
@ -1351,10 +1382,10 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
goto err_close;
|
||||
}
|
||||
|
||||
/* If the mode USES_PRIMARY, then the following is handled by
|
||||
/* If the mode uses primary, then the following is handled by
|
||||
* bond_change_active_slave().
|
||||
*/
|
||||
if (!USES_PRIMARY(bond->params.mode)) {
|
||||
if (!bond_uses_primary(bond)) {
|
||||
/* set promiscuity level to new slave */
|
||||
if (bond_dev->flags & IFF_PROMISC) {
|
||||
res = dev_set_promiscuity(slave_dev, 1);
|
||||
|
|
@ -1377,7 +1408,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
netif_addr_unlock_bh(bond_dev);
|
||||
}
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
/* add lacpdu mc addr to mc list */
|
||||
u8 lacpdu_multicast[ETH_ALEN] = MULTICAST_LACPDU_ADDR;
|
||||
|
||||
|
|
@ -1450,7 +1481,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
new_slave->link == BOND_LINK_DOWN ? "DOWN" :
|
||||
(new_slave->link == BOND_LINK_UP ? "UP" : "BACK"));
|
||||
|
||||
if (USES_PRIMARY(bond->params.mode) && bond->params.primary[0]) {
|
||||
if (bond_uses_primary(bond) && bond->params.primary[0]) {
|
||||
/* if there is a primary slave, remember it */
|
||||
if (strcmp(bond->params.primary, new_slave->dev->name) == 0) {
|
||||
bond->primary_slave = new_slave;
|
||||
|
|
@ -1458,7 +1489,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
}
|
||||
}
|
||||
|
||||
switch (bond->params.mode) {
|
||||
switch (BOND_MODE(bond)) {
|
||||
case BOND_MODE_ACTIVEBACKUP:
|
||||
bond_set_slave_inactive_flags(new_slave,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
|
@ -1471,14 +1502,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
bond_set_slave_inactive_flags(new_slave, BOND_SLAVE_NOTIFY_NOW);
|
||||
/* if this is the first slave */
|
||||
if (!prev_slave) {
|
||||
SLAVE_AD_INFO(new_slave).id = 1;
|
||||
SLAVE_AD_INFO(new_slave)->id = 1;
|
||||
/* Initialize AD with the number of times that the AD timer is called in 1 second
|
||||
* can be called only after the mac address of the bond is set
|
||||
*/
|
||||
bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL);
|
||||
} else {
|
||||
SLAVE_AD_INFO(new_slave).id =
|
||||
SLAVE_AD_INFO(prev_slave).id + 1;
|
||||
SLAVE_AD_INFO(new_slave)->id =
|
||||
SLAVE_AD_INFO(prev_slave)->id + 1;
|
||||
}
|
||||
|
||||
bond_3ad_bind_slave(new_slave);
|
||||
|
|
@ -1539,7 +1570,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
|
|||
bond_compute_features(bond);
|
||||
bond_set_carrier(bond);
|
||||
|
||||
if (USES_PRIMARY(bond->params.mode)) {
|
||||
if (bond_uses_primary(bond)) {
|
||||
block_netpoll_tx();
|
||||
write_lock_bh(&bond->curr_slave_lock);
|
||||
bond_select_active_slave(bond);
|
||||
|
|
@ -1563,7 +1594,7 @@ err_unregister:
|
|||
netdev_rx_handler_unregister(slave_dev);
|
||||
|
||||
err_detach:
|
||||
if (!USES_PRIMARY(bond->params.mode))
|
||||
if (!bond_uses_primary(bond))
|
||||
bond_hw_addr_flush(bond_dev, slave_dev);
|
||||
|
||||
vlan_vids_del_by_dev(slave_dev, bond_dev);
|
||||
|
|
@ -1585,7 +1616,7 @@ err_close:
|
|||
|
||||
err_restore_mac:
|
||||
if (!bond->params.fail_over_mac ||
|
||||
bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
|
||||
BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
|
||||
/* XXX TODO - fom follow mode needs to change master's
|
||||
* MAC if this slave's MAC is in use by the bond, or at
|
||||
* least print a warning.
|
||||
|
|
@ -1599,7 +1630,7 @@ err_restore_mtu:
|
|||
dev_set_mtu(slave_dev, new_slave->original_mtu);
|
||||
|
||||
err_free:
|
||||
kfree(new_slave);
|
||||
bond_free_slave(new_slave);
|
||||
|
||||
err_undo_flags:
|
||||
/* Enslave of first slave has failed and we need to fix master's mac */
|
||||
|
|
@ -1661,7 +1692,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|||
write_lock_bh(&bond->lock);
|
||||
|
||||
/* Inform AD package of unbinding of slave. */
|
||||
if (bond->params.mode == BOND_MODE_8023AD)
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_3ad_unbind_slave(slave);
|
||||
|
||||
write_unlock_bh(&bond->lock);
|
||||
|
|
@ -1676,7 +1707,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|||
bond->current_arp_slave = NULL;
|
||||
|
||||
if (!all && (!bond->params.fail_over_mac ||
|
||||
bond->params.mode != BOND_MODE_ACTIVEBACKUP)) {
|
||||
BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP)) {
|
||||
if (ether_addr_equal_64bits(bond_dev->dev_addr, slave->perm_hwaddr) &&
|
||||
bond_has_slaves(bond))
|
||||
pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s - set the HWaddr of %s to a different address to avoid conflicts\n",
|
||||
|
|
@ -1748,10 +1779,10 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|||
/* must do this from outside any spinlocks */
|
||||
vlan_vids_del_by_dev(slave_dev, bond_dev);
|
||||
|
||||
/* If the mode USES_PRIMARY, then this cases was handled above by
|
||||
/* If the mode uses primary, then this cases was handled above by
|
||||
* bond_change_active_slave(..., NULL)
|
||||
*/
|
||||
if (!USES_PRIMARY(bond->params.mode)) {
|
||||
if (!bond_uses_primary(bond)) {
|
||||
/* unset promiscuity level from slave
|
||||
* NOTE: The NETDEV_CHANGEADDR call above may change the value
|
||||
* of the IFF_PROMISC flag in the bond_dev, but we need the
|
||||
|
|
@ -1775,7 +1806,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|||
dev_close(slave_dev);
|
||||
|
||||
if (bond->params.fail_over_mac != BOND_FOM_ACTIVE ||
|
||||
bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
|
||||
BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
|
||||
/* restore original ("permanent") mac address */
|
||||
ether_addr_copy(addr.sa_data, slave->perm_hwaddr);
|
||||
addr.sa_family = slave_dev->type;
|
||||
|
|
@ -1786,7 +1817,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
|||
|
||||
slave_dev->priv_flags &= ~IFF_BONDING;
|
||||
|
||||
kfree(slave);
|
||||
bond_free_slave(slave);
|
||||
|
||||
return 0; /* deletion OK */
|
||||
}
|
||||
|
|
@ -1821,7 +1852,7 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info)
|
|||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
|
||||
info->bond_mode = bond->params.mode;
|
||||
info->bond_mode = BOND_MODE(bond);
|
||||
info->miimon = bond->params.miimon;
|
||||
|
||||
info->num_slaves = bond->slave_cnt;
|
||||
|
|
@ -1877,7 +1908,7 @@ static int bond_miimon_inspect(struct bonding *bond)
|
|||
if (slave->delay) {
|
||||
pr_info("%s: link status down for %sinterface %s, disabling it in %d ms\n",
|
||||
bond->dev->name,
|
||||
(bond->params.mode ==
|
||||
(BOND_MODE(bond) ==
|
||||
BOND_MODE_ACTIVEBACKUP) ?
|
||||
(bond_is_active_slave(slave) ?
|
||||
"active " : "backup ") : "",
|
||||
|
|
@ -1968,10 +1999,10 @@ static void bond_miimon_commit(struct bonding *bond)
|
|||
slave->link = BOND_LINK_UP;
|
||||
slave->last_link_up = jiffies;
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
/* prevent it from being the active one */
|
||||
bond_set_backup_slave(slave);
|
||||
} else if (bond->params.mode != BOND_MODE_ACTIVEBACKUP) {
|
||||
} else if (BOND_MODE(bond) != BOND_MODE_ACTIVEBACKUP) {
|
||||
/* make it immediately active */
|
||||
bond_set_active_slave(slave);
|
||||
} else if (slave != bond->primary_slave) {
|
||||
|
|
@ -1985,7 +2016,7 @@ static void bond_miimon_commit(struct bonding *bond)
|
|||
slave->duplex ? "full" : "half");
|
||||
|
||||
/* notify ad that the link status has changed */
|
||||
if (bond->params.mode == BOND_MODE_8023AD)
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_3ad_handle_link_change(slave, BOND_LINK_UP);
|
||||
|
||||
if (bond_is_lb(bond))
|
||||
|
|
@ -2004,15 +2035,15 @@ static void bond_miimon_commit(struct bonding *bond)
|
|||
|
||||
slave->link = BOND_LINK_DOWN;
|
||||
|
||||
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP ||
|
||||
bond->params.mode == BOND_MODE_8023AD)
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
|
||||
BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_set_slave_inactive_flags(slave,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
||||
pr_info("%s: link status definitely down for interface %s, disabling it\n",
|
||||
bond->dev->name, slave->dev->name);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD)
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD)
|
||||
bond_3ad_handle_link_change(slave,
|
||||
BOND_LINK_DOWN);
|
||||
|
||||
|
|
@ -2175,9 +2206,9 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op,
|
|||
* When the path is validated, collect any vlan information in the
|
||||
* path.
|
||||
*/
|
||||
static bool bond_verify_device_path(struct net_device *start_dev,
|
||||
struct net_device *end_dev,
|
||||
struct bond_vlan_tag *tags)
|
||||
bool bond_verify_device_path(struct net_device *start_dev,
|
||||
struct net_device *end_dev,
|
||||
struct bond_vlan_tag *tags)
|
||||
{
|
||||
struct net_device *upper;
|
||||
struct list_head *iter;
|
||||
|
|
@ -2287,8 +2318,8 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
|
|||
int alen, is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP);
|
||||
|
||||
if (!slave_do_arp_validate(bond, slave)) {
|
||||
if ((slave_do_arp_validate_only(bond, slave) && is_arp) ||
|
||||
!slave_do_arp_validate_only(bond, slave))
|
||||
if ((slave_do_arp_validate_only(bond) && is_arp) ||
|
||||
!slave_do_arp_validate_only(bond))
|
||||
slave->last_rx = jiffies;
|
||||
return RX_HANDLER_ANOTHER;
|
||||
} else if (!is_arp) {
|
||||
|
|
@ -2456,7 +2487,7 @@ static void bond_loadbalance_arp_mon(struct work_struct *work)
|
|||
* do - all replies will be rx'ed on same link causing slaves
|
||||
* to be unstable during low/no traffic periods
|
||||
*/
|
||||
if (IS_UP(slave->dev))
|
||||
if (bond_slave_is_up(slave))
|
||||
bond_arp_send_all(bond, slave);
|
||||
}
|
||||
|
||||
|
|
@ -2678,10 +2709,10 @@ static bool bond_ab_arp_probe(struct bonding *bond)
|
|||
bond_set_slave_inactive_flags(curr_arp_slave, BOND_SLAVE_NOTIFY_LATER);
|
||||
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (!found && !before && IS_UP(slave->dev))
|
||||
if (!found && !before && bond_slave_is_up(slave))
|
||||
before = slave;
|
||||
|
||||
if (found && !new_slave && IS_UP(slave->dev))
|
||||
if (found && !new_slave && bond_slave_is_up(slave))
|
||||
new_slave = slave;
|
||||
/* if the link state is up at this point, we
|
||||
* mark it down - this can happen if we have
|
||||
|
|
@ -2690,7 +2721,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
|
|||
* one the current slave so it is still marked
|
||||
* up when it is actually down
|
||||
*/
|
||||
if (!IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
|
||||
if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
|
||||
slave->link = BOND_LINK_DOWN;
|
||||
if (slave->link_failure_count < UINT_MAX)
|
||||
slave->link_failure_count++;
|
||||
|
|
@ -2853,7 +2884,7 @@ static int bond_slave_netdev_event(unsigned long event,
|
|||
|
||||
bond_update_speed_duplex(slave);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
if (old_speed != slave->speed)
|
||||
bond_3ad_adapter_speed_changed(slave);
|
||||
if (old_duplex != slave->duplex)
|
||||
|
|
@ -2881,7 +2912,7 @@ static int bond_slave_netdev_event(unsigned long event,
|
|||
break;
|
||||
case NETDEV_CHANGENAME:
|
||||
/* we don't care if we don't have primary set */
|
||||
if (!USES_PRIMARY(bond->params.mode) ||
|
||||
if (!bond_uses_primary(bond) ||
|
||||
!bond->params.primary[0])
|
||||
break;
|
||||
|
||||
|
|
@ -3011,20 +3042,18 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
|
|||
* bond_xmit_hash - generate a hash value based on the xmit policy
|
||||
* @bond: bonding device
|
||||
* @skb: buffer to use for headers
|
||||
* @count: modulo value
|
||||
*
|
||||
* This function will extract the necessary headers from the skb buffer and use
|
||||
* them to generate a hash based on the xmit_policy set in the bonding device
|
||||
* which will be reduced modulo count before returning.
|
||||
*/
|
||||
int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count)
|
||||
u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
|
||||
{
|
||||
struct flow_keys flow;
|
||||
u32 hash;
|
||||
|
||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
|
||||
!bond_flow_dissect(bond, skb, &flow))
|
||||
return bond_eth_hash(skb) % count;
|
||||
return bond_eth_hash(skb);
|
||||
|
||||
if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23 ||
|
||||
bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23)
|
||||
|
|
@ -3035,7 +3064,7 @@ int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count)
|
|||
hash ^= (hash >> 16);
|
||||
hash ^= (hash >> 8);
|
||||
|
||||
return hash % count;
|
||||
return hash;
|
||||
}
|
||||
|
||||
/*-------------------------- Device entry points ----------------------------*/
|
||||
|
|
@ -3046,7 +3075,7 @@ static void bond_work_init_all(struct bonding *bond)
|
|||
bond_resend_igmp_join_requests_delayed);
|
||||
INIT_DELAYED_WORK(&bond->alb_work, bond_alb_monitor);
|
||||
INIT_DELAYED_WORK(&bond->mii_work, bond_mii_monitor);
|
||||
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP)
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
|
||||
INIT_DELAYED_WORK(&bond->arp_work, bond_activebackup_arp_mon);
|
||||
else
|
||||
INIT_DELAYED_WORK(&bond->arp_work, bond_loadbalance_arp_mon);
|
||||
|
|
@ -3073,7 +3102,7 @@ static int bond_open(struct net_device *bond_dev)
|
|||
if (bond_has_slaves(bond)) {
|
||||
read_lock(&bond->curr_slave_lock);
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
if (USES_PRIMARY(bond->params.mode)
|
||||
if (bond_uses_primary(bond)
|
||||
&& (slave != bond->curr_active_slave)) {
|
||||
bond_set_slave_inactive_flags(slave,
|
||||
BOND_SLAVE_NOTIFY_NOW);
|
||||
|
|
@ -3092,9 +3121,10 @@ static int bond_open(struct net_device *bond_dev)
|
|||
/* bond_alb_initialize must be called before the timer
|
||||
* is started.
|
||||
*/
|
||||
if (bond_alb_initialize(bond, (bond->params.mode == BOND_MODE_ALB)))
|
||||
if (bond_alb_initialize(bond, (BOND_MODE(bond) == BOND_MODE_ALB)))
|
||||
return -ENOMEM;
|
||||
queue_delayed_work(bond->wq, &bond->alb_work, 0);
|
||||
if (bond->params.tlb_dynamic_lb)
|
||||
queue_delayed_work(bond->wq, &bond->alb_work, 0);
|
||||
}
|
||||
|
||||
if (bond->params.miimon) /* link check interval, in milliseconds. */
|
||||
|
|
@ -3105,7 +3135,7 @@ static int bond_open(struct net_device *bond_dev)
|
|||
bond->recv_probe = bond_arp_rcv;
|
||||
}
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
queue_delayed_work(bond->wq, &bond->ad_work, 0);
|
||||
/* register to receive LACPDUs */
|
||||
bond->recv_probe = bond_3ad_lacpdu_recv;
|
||||
|
|
@ -3310,7 +3340,7 @@ static void bond_set_rx_mode(struct net_device *bond_dev)
|
|||
|
||||
|
||||
rcu_read_lock();
|
||||
if (USES_PRIMARY(bond->params.mode)) {
|
||||
if (bond_uses_primary(bond)) {
|
||||
slave = rcu_dereference(bond->curr_active_slave);
|
||||
if (slave) {
|
||||
dev_uc_sync(slave->dev, bond_dev);
|
||||
|
|
@ -3464,7 +3494,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
|
|||
struct list_head *iter;
|
||||
int res = 0;
|
||||
|
||||
if (bond->params.mode == BOND_MODE_ALB)
|
||||
if (BOND_MODE(bond) == BOND_MODE_ALB)
|
||||
return bond_alb_set_mac_address(bond_dev, addr);
|
||||
|
||||
|
||||
|
|
@ -3475,7 +3505,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr)
|
|||
* Returning an error causes ifenslave to fail.
|
||||
*/
|
||||
if (bond->params.fail_over_mac &&
|
||||
bond->params.mode == BOND_MODE_ACTIVEBACKUP)
|
||||
BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
|
||||
return 0;
|
||||
|
||||
if (!is_valid_ether_addr(sa->sa_data))
|
||||
|
|
@ -3555,7 +3585,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
|
|||
/* Here we start from the slave with slave_id */
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (--i < 0) {
|
||||
if (slave_can_tx(slave)) {
|
||||
if (bond_slave_can_tx(slave)) {
|
||||
bond_dev_queue_xmit(bond, skb, slave->dev);
|
||||
return;
|
||||
}
|
||||
|
|
@ -3567,7 +3597,7 @@ static void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int sl
|
|||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (--i < 0)
|
||||
break;
|
||||
if (slave_can_tx(slave)) {
|
||||
if (bond_slave_can_tx(slave)) {
|
||||
bond_dev_queue_xmit(bond, skb, slave->dev);
|
||||
return;
|
||||
}
|
||||
|
|
@ -3624,7 +3654,7 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev
|
|||
*/
|
||||
if (iph->protocol == IPPROTO_IGMP && skb->protocol == htons(ETH_P_IP)) {
|
||||
slave = rcu_dereference(bond->curr_active_slave);
|
||||
if (slave && slave_can_tx(slave))
|
||||
if (slave && bond_slave_can_tx(slave))
|
||||
bond_dev_queue_xmit(bond, skb, slave->dev);
|
||||
else
|
||||
bond_xmit_slave_id(bond, skb, 0);
|
||||
|
|
@ -3662,7 +3692,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
{
|
||||
struct bonding *bond = netdev_priv(bond_dev);
|
||||
|
||||
bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb, bond->slave_cnt));
|
||||
bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb) % bond->slave_cnt);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
|
@ -3677,7 +3707,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (bond_is_last_slave(bond, slave))
|
||||
break;
|
||||
if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP) {
|
||||
if (bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) {
|
||||
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
|
||||
|
||||
if (!skb2) {
|
||||
|
|
@ -3689,7 +3719,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev)
|
|||
bond_dev_queue_xmit(bond, skb2, slave->dev);
|
||||
}
|
||||
}
|
||||
if (slave && IS_UP(slave->dev) && slave->link == BOND_LINK_UP)
|
||||
if (slave && bond_slave_is_up(slave) && slave->link == BOND_LINK_UP)
|
||||
bond_dev_queue_xmit(bond, skb, slave->dev);
|
||||
else
|
||||
dev_kfree_skb_any(skb);
|
||||
|
|
@ -3714,7 +3744,7 @@ static inline int bond_slave_override(struct bonding *bond,
|
|||
/* Find out if any slaves have the same mapping as this skb. */
|
||||
bond_for_each_slave_rcu(bond, slave, iter) {
|
||||
if (slave->queue_id == skb->queue_mapping) {
|
||||
if (slave_can_tx(slave)) {
|
||||
if (bond_slave_can_tx(slave)) {
|
||||
bond_dev_queue_xmit(bond, skb, slave->dev);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3755,12 +3785,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
|
|||
{
|
||||
struct bonding *bond = netdev_priv(dev);
|
||||
|
||||
if (TX_QUEUE_OVERRIDE(bond->params.mode)) {
|
||||
if (!bond_slave_override(bond, skb))
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
if (bond_should_override_tx_queue(bond) &&
|
||||
!bond_slave_override(bond, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
switch (bond->params.mode) {
|
||||
switch (BOND_MODE(bond)) {
|
||||
case BOND_MODE_ROUNDROBIN:
|
||||
return bond_xmit_roundrobin(skb, dev);
|
||||
case BOND_MODE_ACTIVEBACKUP:
|
||||
|
|
@ -3772,12 +3801,13 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
|
|||
case BOND_MODE_8023AD:
|
||||
return bond_3ad_xmit_xor(skb, dev);
|
||||
case BOND_MODE_ALB:
|
||||
case BOND_MODE_TLB:
|
||||
return bond_alb_xmit(skb, dev);
|
||||
case BOND_MODE_TLB:
|
||||
return bond_tlb_xmit(skb, dev);
|
||||
default:
|
||||
/* Should never happen, mode already checked */
|
||||
pr_err("%s: Error: Unknown bonding mode %d\n",
|
||||
dev->name, bond->params.mode);
|
||||
dev->name, BOND_MODE(bond));
|
||||
WARN_ON_ONCE(1);
|
||||
dev_kfree_skb_any(skb);
|
||||
return NETDEV_TX_OK;
|
||||
|
|
@ -3817,14 +3847,14 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev,
|
|||
ecmd->duplex = DUPLEX_UNKNOWN;
|
||||
ecmd->port = PORT_OTHER;
|
||||
|
||||
/* Since SLAVE_IS_OK returns false for all inactive or down slaves, we
|
||||
/* Since bond_slave_can_tx returns false for all inactive or down slaves, we
|
||||
* do not need to check mode. Though link speed might not represent
|
||||
* the true receive or transmit bandwidth (not all modes are symmetric)
|
||||
* this is an accurate maximum.
|
||||
*/
|
||||
read_lock(&bond->lock);
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
if (SLAVE_IS_OK(slave)) {
|
||||
if (bond_slave_can_tx(slave)) {
|
||||
if (slave->speed != SPEED_UNKNOWN)
|
||||
speed += slave->speed;
|
||||
if (ecmd->duplex == DUPLEX_UNKNOWN &&
|
||||
|
|
@ -3915,7 +3945,7 @@ void bond_setup(struct net_device *bond_dev)
|
|||
/* Initialize the device options */
|
||||
bond_dev->tx_queue_len = 0;
|
||||
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
|
||||
bond_dev->priv_flags |= IFF_BONDING;
|
||||
bond_dev->priv_flags |= IFF_BONDING | IFF_UNICAST_FLT;
|
||||
bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
|
||||
|
||||
/* At first, we block adding VLANs. That's the only way to
|
||||
|
|
@ -3994,7 +4024,8 @@ static int bond_check_params(struct bond_params *params)
|
|||
|
||||
if (xmit_hash_policy) {
|
||||
if ((bond_mode != BOND_MODE_XOR) &&
|
||||
(bond_mode != BOND_MODE_8023AD)) {
|
||||
(bond_mode != BOND_MODE_8023AD) &&
|
||||
(bond_mode != BOND_MODE_TLB)) {
|
||||
pr_info("xmit_hash_policy param is irrelevant in mode %s\n",
|
||||
bond_mode_name(bond_mode));
|
||||
} else {
|
||||
|
|
@ -4079,7 +4110,7 @@ static int bond_check_params(struct bond_params *params)
|
|||
}
|
||||
|
||||
/* reset values for 802.3ad/TLB/ALB */
|
||||
if (BOND_NO_USES_ARP(bond_mode)) {
|
||||
if (!bond_mode_uses_arp(bond_mode)) {
|
||||
if (!miimon) {
|
||||
pr_warn("Warning: miimon must be specified, otherwise bonding will not detect link failure, speed and duplex which are essential for 802.3ad operation\n");
|
||||
pr_warn("Forcing miimon to 100msec\n");
|
||||
|
|
@ -4161,7 +4192,7 @@ static int bond_check_params(struct bond_params *params)
|
|||
catch mistakes */
|
||||
__be32 ip;
|
||||
if (!in4_pton(arp_ip_target[i], -1, (u8 *)&ip, -1, NULL) ||
|
||||
IS_IP_TARGET_UNUSABLE_ADDRESS(ip)) {
|
||||
!bond_is_ip_target_ok(ip)) {
|
||||
pr_warn("Warning: bad arp_ip_target module parameter (%s), ARP monitoring will not be performed\n",
|
||||
arp_ip_target[i]);
|
||||
arp_interval = 0;
|
||||
|
|
@ -4234,7 +4265,7 @@ static int bond_check_params(struct bond_params *params)
|
|||
pr_debug("Warning: either miimon or arp_interval and arp_ip_target module parameters must be specified, otherwise bonding will not detect link failures! see bonding.txt for details\n");
|
||||
}
|
||||
|
||||
if (primary && !USES_PRIMARY(bond_mode)) {
|
||||
if (primary && !bond_mode_uses_primary(bond_mode)) {
|
||||
/* currently, using a primary only makes sense
|
||||
* in active backup, TLB or ALB modes
|
||||
*/
|
||||
|
|
@ -4300,6 +4331,7 @@ static int bond_check_params(struct bond_params *params)
|
|||
params->min_links = min_links;
|
||||
params->lp_interval = lp_interval;
|
||||
params->packets_per_slave = packets_per_slave;
|
||||
params->tlb_dynamic_lb = 1; /* Default value */
|
||||
if (packets_per_slave > 0) {
|
||||
params->reciprocal_packets_per_slave =
|
||||
reciprocal_value(packets_per_slave);
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ static int bond_fill_slave_info(struct sk_buff *skb,
|
|||
if (nla_put_u16(skb, IFLA_BOND_SLAVE_QUEUE_ID, slave->queue_id))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (slave->bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
|
||||
const struct aggregator *agg;
|
||||
|
||||
agg = SLAVE_AD_INFO(slave).port.aggregator;
|
||||
agg = SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
if (agg)
|
||||
if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
|
||||
agg->aggregator_identifier))
|
||||
|
|
@ -407,7 +407,7 @@ static int bond_fill_info(struct sk_buff *skb,
|
|||
unsigned int packets_per_slave;
|
||||
int i, targets_added;
|
||||
|
||||
if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode))
|
||||
if (nla_put_u8(skb, IFLA_BOND_MODE, BOND_MODE(bond)))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (slave_dev &&
|
||||
|
|
@ -505,7 +505,7 @@ static int bond_fill_info(struct sk_buff *skb,
|
|||
bond->params.ad_select))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info info;
|
||||
|
||||
if (!bond_3ad_get_active_agg_info(bond, &info)) {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ static int bond_option_mode_set(struct bonding *bond,
|
|||
const struct bond_opt_value *newval);
|
||||
static int bond_option_slaves_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval);
|
||||
static int bond_option_tlb_dynamic_lb_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval);
|
||||
|
||||
|
||||
static const struct bond_opt_value bond_mode_tbl[] = {
|
||||
|
|
@ -180,6 +182,12 @@ static const struct bond_opt_value bond_lp_interval_tbl[] = {
|
|||
{ NULL, -1, 0},
|
||||
};
|
||||
|
||||
static const struct bond_opt_value bond_tlb_dynamic_lb_tbl[] = {
|
||||
{ "off", 0, 0},
|
||||
{ "on", 1, BOND_VALFLAG_DEFAULT},
|
||||
{ NULL, -1, 0}
|
||||
};
|
||||
|
||||
static const struct bond_option bond_opts[] = {
|
||||
[BOND_OPT_MODE] = {
|
||||
.id = BOND_OPT_MODE,
|
||||
|
|
@ -200,7 +208,7 @@ static const struct bond_option bond_opts[] = {
|
|||
[BOND_OPT_XMIT_HASH] = {
|
||||
.id = BOND_OPT_XMIT_HASH,
|
||||
.name = "xmit_hash_policy",
|
||||
.desc = "balance-xor and 802.3ad hashing method",
|
||||
.desc = "balance-xor, 802.3ad, and tlb hashing method",
|
||||
.values = bond_xmit_hashtype_tbl,
|
||||
.set = bond_option_xmit_hash_policy_set
|
||||
},
|
||||
|
|
@ -365,9 +373,33 @@ static const struct bond_option bond_opts[] = {
|
|||
.flags = BOND_OPTFLAG_RAWVAL,
|
||||
.set = bond_option_slaves_set
|
||||
},
|
||||
[BOND_OPT_TLB_DYNAMIC_LB] = {
|
||||
.id = BOND_OPT_TLB_DYNAMIC_LB,
|
||||
.name = "tlb_dynamic_lb",
|
||||
.desc = "Enable dynamic flow shuffling",
|
||||
.unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_TLB)),
|
||||
.values = bond_tlb_dynamic_lb_tbl,
|
||||
.flags = BOND_OPTFLAG_IFDOWN,
|
||||
.set = bond_option_tlb_dynamic_lb_set,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
/* Searches for an option by name */
|
||||
const struct bond_option *bond_opt_get_by_name(const char *name)
|
||||
{
|
||||
const struct bond_option *opt;
|
||||
int option;
|
||||
|
||||
for (option = 0; option < BOND_OPT_LAST; option++) {
|
||||
opt = bond_opt_get(option);
|
||||
if (opt && !strcmp(opt->name, name))
|
||||
return opt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Searches for a value in opt's values[] table */
|
||||
const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val)
|
||||
{
|
||||
|
|
@ -641,7 +673,7 @@ const struct bond_option *bond_opt_get(unsigned int option)
|
|||
|
||||
int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newval)
|
||||
{
|
||||
if (BOND_NO_USES_ARP(newval->value) && bond->params.arp_interval) {
|
||||
if (!bond_mode_uses_arp(newval->value) && bond->params.arp_interval) {
|
||||
pr_info("%s: %s mode is incompatible with arp monitoring, start mii monitoring\n",
|
||||
bond->dev->name, newval->string);
|
||||
/* disable arp monitoring */
|
||||
|
|
@ -662,7 +694,7 @@ int bond_option_mode_set(struct bonding *bond, const struct bond_opt_value *newv
|
|||
static struct net_device *__bond_option_active_slave_get(struct bonding *bond,
|
||||
struct slave *slave)
|
||||
{
|
||||
return USES_PRIMARY(bond->params.mode) && slave ? slave->dev : NULL;
|
||||
return bond_uses_primary(bond) && slave ? slave->dev : NULL;
|
||||
}
|
||||
|
||||
struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond)
|
||||
|
|
@ -727,7 +759,7 @@ static int bond_option_active_slave_set(struct bonding *bond,
|
|||
bond->dev->name, new_active->dev->name);
|
||||
} else {
|
||||
if (old_active && (new_active->link == BOND_LINK_UP) &&
|
||||
IS_UP(new_active->dev)) {
|
||||
bond_slave_is_up(new_active)) {
|
||||
pr_info("%s: Setting %s as active slave\n",
|
||||
bond->dev->name, new_active->dev->name);
|
||||
bond_change_active_slave(bond, new_active);
|
||||
|
|
@ -746,6 +778,10 @@ static int bond_option_active_slave_set(struct bonding *bond,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* There are two tricky bits here. First, if MII monitoring is activated, then
|
||||
* we must disable ARP monitoring. Second, if the timer isn't running, we must
|
||||
* start it.
|
||||
*/
|
||||
static int bond_option_miimon_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval)
|
||||
{
|
||||
|
|
@ -784,6 +820,10 @@ static int bond_option_miimon_set(struct bonding *bond,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Set up and down delays. These must be multiples of the
|
||||
* MII monitoring value, and are stored internally as the multiplier.
|
||||
* Thus, we must translate to MS for the real world.
|
||||
*/
|
||||
static int bond_option_updelay_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval)
|
||||
{
|
||||
|
|
@ -842,6 +882,10 @@ static int bond_option_use_carrier_set(struct bonding *bond,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* There are two tricky bits here. First, if ARP monitoring is activated, then
|
||||
* we must disable MII monitoring. Second, if the ARP timer isn't running,
|
||||
* we must start it.
|
||||
*/
|
||||
static int bond_option_arp_interval_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval)
|
||||
{
|
||||
|
|
@ -899,7 +943,7 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
|
|||
__be32 *targets = bond->params.arp_targets;
|
||||
int ind;
|
||||
|
||||
if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
|
||||
if (!bond_is_ip_target_ok(target)) {
|
||||
pr_err("%s: invalid ARP target %pI4 specified for addition\n",
|
||||
bond->dev->name, &target);
|
||||
return -EINVAL;
|
||||
|
|
@ -944,7 +988,7 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
|
|||
unsigned long *targets_rx;
|
||||
int ind, i;
|
||||
|
||||
if (IS_IP_TARGET_UNUSABLE_ADDRESS(target)) {
|
||||
if (!bond_is_ip_target_ok(target)) {
|
||||
pr_err("%s: invalid ARP target %pI4 specified for removal\n",
|
||||
bond->dev->name, &target);
|
||||
return -EINVAL;
|
||||
|
|
@ -1338,3 +1382,13 @@ err_no_cmd:
|
|||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int bond_option_tlb_dynamic_lb_set(struct bonding *bond,
|
||||
const struct bond_opt_value *newval)
|
||||
{
|
||||
pr_info("%s: Setting dynamic-lb to %s (%llu)\n",
|
||||
bond->dev->name, newval->string, newval->value);
|
||||
bond->params.tlb_dynamic_lb = newval->value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ enum {
|
|||
BOND_OPT_RESEND_IGMP,
|
||||
BOND_OPT_LP_INTERVAL,
|
||||
BOND_OPT_SLAVES,
|
||||
BOND_OPT_TLB_DYNAMIC_LB,
|
||||
BOND_OPT_LAST
|
||||
};
|
||||
|
||||
|
|
@ -104,6 +105,7 @@ int bond_opt_tryset_rtnl(struct bonding *bond, unsigned int option, char *buf);
|
|||
const struct bond_opt_value *bond_opt_parse(const struct bond_option *opt,
|
||||
struct bond_opt_value *val);
|
||||
const struct bond_option *bond_opt_get(unsigned int option);
|
||||
const struct bond_option *bond_opt_get_by_name(const char *name);
|
||||
const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val);
|
||||
|
||||
/* This helper is used to initialize a bond_opt_value structure for parameter
|
||||
|
|
|
|||
|
|
@ -72,9 +72,9 @@ static void bond_info_show_master(struct seq_file *seq)
|
|||
curr = rcu_dereference(bond->curr_active_slave);
|
||||
|
||||
seq_printf(seq, "Bonding Mode: %s",
|
||||
bond_mode_name(bond->params.mode));
|
||||
bond_mode_name(BOND_MODE(bond)));
|
||||
|
||||
if (bond->params.mode == BOND_MODE_ACTIVEBACKUP &&
|
||||
if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP &&
|
||||
bond->params.fail_over_mac) {
|
||||
optval = bond_opt_get_val(BOND_OPT_FAIL_OVER_MAC,
|
||||
bond->params.fail_over_mac);
|
||||
|
|
@ -83,15 +83,15 @@ static void bond_info_show_master(struct seq_file *seq)
|
|||
|
||||
seq_printf(seq, "\n");
|
||||
|
||||
if (bond->params.mode == BOND_MODE_XOR ||
|
||||
bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_XOR ||
|
||||
BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
optval = bond_opt_get_val(BOND_OPT_XMIT_HASH,
|
||||
bond->params.xmit_policy);
|
||||
seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
|
||||
optval->string, bond->params.xmit_policy);
|
||||
}
|
||||
|
||||
if (USES_PRIMARY(bond->params.mode)) {
|
||||
if (bond_uses_primary(bond)) {
|
||||
seq_printf(seq, "Primary Slave: %s",
|
||||
(bond->primary_slave) ?
|
||||
bond->primary_slave->dev->name : "None");
|
||||
|
|
@ -134,7 +134,7 @@ static void bond_info_show_master(struct seq_file *seq)
|
|||
seq_printf(seq, "\n");
|
||||
}
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info ad_info;
|
||||
|
||||
seq_puts(seq, "\n802.3ad info\n");
|
||||
|
|
@ -188,9 +188,9 @@ static void bond_info_show_slave(struct seq_file *seq,
|
|||
|
||||
seq_printf(seq, "Permanent HW addr: %pM\n", slave->perm_hwaddr);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
const struct aggregator *agg
|
||||
= SLAVE_AD_INFO(slave).port.aggregator;
|
||||
= SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
|
||||
if (agg)
|
||||
seq_printf(seq, "Aggregator ID: %d\n",
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@
|
|||
#define to_dev(obj) container_of(obj, struct device, kobj)
|
||||
#define to_bond(cd) ((struct bonding *)(netdev_priv(to_net_dev(cd))))
|
||||
|
||||
/*
|
||||
* "show" function for the bond_masters attribute.
|
||||
/* "show" function for the bond_masters attribute.
|
||||
* The class parameter is ignored.
|
||||
*/
|
||||
static ssize_t bonding_show_bonds(struct class *cls,
|
||||
|
|
@ -88,14 +87,12 @@ static struct net_device *bond_get_by_name(struct bond_net *bn, const char *ifna
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "store" function for the bond_masters attribute. This is what
|
||||
/* "store" function for the bond_masters attribute. This is what
|
||||
* creates and deletes entire bonds.
|
||||
*
|
||||
* The class parameter is ignored.
|
||||
*
|
||||
*/
|
||||
|
||||
static ssize_t bonding_store_bonds(struct class *cls,
|
||||
struct class_attribute *attr,
|
||||
const char *buffer, size_t count)
|
||||
|
|
@ -158,9 +155,26 @@ static const struct class_attribute class_attr_bonding_masters = {
|
|||
.store = bonding_store_bonds,
|
||||
};
|
||||
|
||||
/*
|
||||
* Show the slaves in the current bond.
|
||||
*/
|
||||
/* Generic "store" method for bonding sysfs option setting */
|
||||
static ssize_t bonding_sysfs_store_option(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buffer, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
const struct bond_option *opt;
|
||||
int ret;
|
||||
|
||||
opt = bond_opt_get_by_name(attr->attr.name);
|
||||
if (WARN_ON(!opt))
|
||||
return -ENOENT;
|
||||
ret = bond_opt_tryset_rtnl(bond, opt->id, (char *)buffer);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Show the slaves in the current bond. */
|
||||
static ssize_t bonding_show_slaves(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
|
@ -190,62 +204,24 @@ static ssize_t bonding_show_slaves(struct device *d,
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the slaves in the current bond.
|
||||
* This is supposed to be only thin wrapper for bond_enslave and bond_release.
|
||||
* All hard work should be done there.
|
||||
*/
|
||||
static ssize_t bonding_store_slaves(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buffer, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_SLAVES, (char *)buffer);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(slaves, S_IRUGO | S_IWUSR, bonding_show_slaves,
|
||||
bonding_store_slaves);
|
||||
bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the bonding mode. The bond interface must be down to
|
||||
* change the mode.
|
||||
*/
|
||||
/* Show the bonding mode. */
|
||||
static ssize_t bonding_show_mode(struct device *d,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
const struct bond_opt_value *val;
|
||||
|
||||
val = bond_opt_get_val(BOND_OPT_MODE, bond->params.mode);
|
||||
val = bond_opt_get_val(BOND_OPT_MODE, BOND_MODE(bond));
|
||||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.mode);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_mode(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MODE, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
return sprintf(buf, "%s %d\n", val->string, BOND_MODE(bond));
|
||||
}
|
||||
static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR,
|
||||
bonding_show_mode, bonding_store_mode);
|
||||
bonding_show_mode, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the bonding transmit hash method.
|
||||
*/
|
||||
/* Show the bonding transmit hash method. */
|
||||
static ssize_t bonding_show_xmit_hash(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -257,26 +233,10 @@ static ssize_t bonding_show_xmit_hash(struct device *d,
|
|||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.xmit_policy);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_xmit_hash(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_XMIT_HASH, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR,
|
||||
bonding_show_xmit_hash, bonding_store_xmit_hash);
|
||||
bonding_show_xmit_hash, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set arp_validate.
|
||||
*/
|
||||
/* Show arp_validate. */
|
||||
static ssize_t bonding_show_arp_validate(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -289,26 +249,10 @@ static ssize_t bonding_show_arp_validate(struct device *d,
|
|||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.arp_validate);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_arp_validate(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_VALIDATE, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate,
|
||||
bonding_store_arp_validate);
|
||||
/*
|
||||
* Show and set arp_all_targets.
|
||||
*/
|
||||
bonding_sysfs_store_option);
|
||||
|
||||
/* Show arp_all_targets. */
|
||||
static ssize_t bonding_show_arp_all_targets(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -321,28 +265,10 @@ static ssize_t bonding_show_arp_all_targets(struct device *d,
|
|||
return sprintf(buf, "%s %d\n",
|
||||
val->string, bond->params.arp_all_targets);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_arp_all_targets(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_ALL_TARGETS, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(arp_all_targets, S_IRUGO | S_IWUSR,
|
||||
bonding_show_arp_all_targets, bonding_store_arp_all_targets);
|
||||
bonding_show_arp_all_targets, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and store fail_over_mac. User only allowed to change the
|
||||
* value when there are no slaves.
|
||||
*/
|
||||
/* Show fail_over_mac. */
|
||||
static ssize_t bonding_show_fail_over_mac(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -355,30 +281,10 @@ static ssize_t bonding_show_fail_over_mac(struct device *d,
|
|||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.fail_over_mac);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_fail_over_mac(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_FAIL_OVER_MAC, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(fail_over_mac, S_IRUGO | S_IWUSR,
|
||||
bonding_show_fail_over_mac, bonding_store_fail_over_mac);
|
||||
bonding_show_fail_over_mac, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the arp timer interval. There are two tricky bits
|
||||
* here. First, if ARP monitoring is activated, then we must disable
|
||||
* MII monitoring. Second, if the ARP timer isn't running, we must
|
||||
* start it.
|
||||
*/
|
||||
/* Show the arp timer interval. */
|
||||
static ssize_t bonding_show_arp_interval(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -387,26 +293,10 @@ static ssize_t bonding_show_arp_interval(struct device *d,
|
|||
|
||||
return sprintf(buf, "%d\n", bond->params.arp_interval);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_arp_interval(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_INTERVAL, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR,
|
||||
bonding_show_arp_interval, bonding_store_arp_interval);
|
||||
bonding_show_arp_interval, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the arp targets.
|
||||
*/
|
||||
/* Show the arp targets. */
|
||||
static ssize_t bonding_show_arp_targets(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -424,27 +314,10 @@ static ssize_t bonding_show_arp_targets(struct device *d,
|
|||
|
||||
return res;
|
||||
}
|
||||
static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR,
|
||||
bonding_show_arp_targets, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_store_arp_targets(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ARP_TARGETS, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets);
|
||||
|
||||
/*
|
||||
* Show and set the up and down delays. These must be multiples of the
|
||||
* MII monitoring value, and are stored internally as the multiplier.
|
||||
* Thus, we must translate to MS for the real world.
|
||||
*/
|
||||
/* Show the up and down delays. */
|
||||
static ssize_t bonding_show_downdelay(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -453,22 +326,8 @@ static ssize_t bonding_show_downdelay(struct device *d,
|
|||
|
||||
return sprintf(buf, "%d\n", bond->params.downdelay * bond->params.miimon);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_downdelay(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_DOWNDELAY, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR,
|
||||
bonding_show_downdelay, bonding_store_downdelay);
|
||||
bonding_show_downdelay, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_updelay(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
|
|
@ -479,27 +338,10 @@ static ssize_t bonding_show_updelay(struct device *d,
|
|||
return sprintf(buf, "%d\n", bond->params.updelay * bond->params.miimon);
|
||||
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_updelay(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_UPDELAY, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR,
|
||||
bonding_show_updelay, bonding_store_updelay);
|
||||
bonding_show_updelay, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the LACP interval. Interface must be down, and the mode
|
||||
* must be set to 802.3ad mode.
|
||||
*/
|
||||
/* Show the LACP interval. */
|
||||
static ssize_t bonding_show_lacp(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -511,22 +353,8 @@ static ssize_t bonding_show_lacp(struct device *d,
|
|||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.lacp_fast);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_lacp(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LACP_RATE, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR,
|
||||
bonding_show_lacp, bonding_store_lacp);
|
||||
bonding_show_lacp, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_min_links(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
|
|
@ -536,22 +364,8 @@ static ssize_t bonding_show_min_links(struct device *d,
|
|||
|
||||
return sprintf(buf, "%u\n", bond->params.min_links);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_min_links(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MINLINKS, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(min_links, S_IRUGO | S_IWUSR,
|
||||
bonding_show_min_links, bonding_store_min_links);
|
||||
bonding_show_min_links, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_ad_select(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
|
|
@ -564,27 +378,10 @@ static ssize_t bonding_show_ad_select(struct device *d,
|
|||
|
||||
return sprintf(buf, "%s %d\n", val->string, bond->params.ad_select);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t bonding_store_ad_select(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_AD_SELECT, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(ad_select, S_IRUGO | S_IWUSR,
|
||||
bonding_show_ad_select, bonding_store_ad_select);
|
||||
bonding_show_ad_select, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the number of peer notifications to send after a failover event.
|
||||
*/
|
||||
/* Show and set the number of peer notifications to send after a failover event. */
|
||||
static ssize_t bonding_show_num_peer_notif(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -611,12 +408,7 @@ static DEVICE_ATTR(num_grat_arp, S_IRUGO | S_IWUSR,
|
|||
static DEVICE_ATTR(num_unsol_na, S_IRUGO | S_IWUSR,
|
||||
bonding_show_num_peer_notif, bonding_store_num_peer_notif);
|
||||
|
||||
/*
|
||||
* Show and set the MII monitor interval. There are two tricky bits
|
||||
* here. First, if MII monitoring is activated, then we must disable
|
||||
* ARP monitoring. Second, if the timer isn't running, we must
|
||||
* start it.
|
||||
*/
|
||||
/* Show the MII monitor interval. */
|
||||
static ssize_t bonding_show_miimon(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -625,30 +417,10 @@ static ssize_t bonding_show_miimon(struct device *d,
|
|||
|
||||
return sprintf(buf, "%d\n", bond->params.miimon);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_miimon(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_MIIMON, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR,
|
||||
bonding_show_miimon, bonding_store_miimon);
|
||||
bonding_show_miimon, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the primary slave. The store function is much
|
||||
* simpler than bonding_store_slaves function because it only needs to
|
||||
* handle one interface name.
|
||||
* The bond must be a mode that supports a primary for this be
|
||||
* set.
|
||||
*/
|
||||
/* Show the primary slave. */
|
||||
static ssize_t bonding_show_primary(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -661,26 +433,10 @@ static ssize_t bonding_show_primary(struct device *d,
|
|||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_primary(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(primary, S_IRUGO | S_IWUSR,
|
||||
bonding_show_primary, bonding_store_primary);
|
||||
bonding_show_primary, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the primary_reselect flag.
|
||||
*/
|
||||
/* Show the primary_reselect flag. */
|
||||
static ssize_t bonding_show_primary_reselect(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -694,28 +450,10 @@ static ssize_t bonding_show_primary_reselect(struct device *d,
|
|||
return sprintf(buf, "%s %d\n",
|
||||
val->string, bond->params.primary_reselect);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_primary_reselect(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PRIMARY_RESELECT,
|
||||
(char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(primary_reselect, S_IRUGO | S_IWUSR,
|
||||
bonding_show_primary_reselect,
|
||||
bonding_store_primary_reselect);
|
||||
bonding_show_primary_reselect, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the use_carrier flag.
|
||||
*/
|
||||
/* Show the use_carrier flag. */
|
||||
static ssize_t bonding_show_carrier(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -724,27 +462,11 @@ static ssize_t bonding_show_carrier(struct device *d,
|
|||
|
||||
return sprintf(buf, "%d\n", bond->params.use_carrier);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_carrier(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_USE_CARRIER, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(use_carrier, S_IRUGO | S_IWUSR,
|
||||
bonding_show_carrier, bonding_store_carrier);
|
||||
bonding_show_carrier, bonding_sysfs_store_option);
|
||||
|
||||
|
||||
/*
|
||||
* Show and set currently active_slave.
|
||||
*/
|
||||
/* Show currently active_slave. */
|
||||
static ssize_t bonding_show_active_slave(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -761,27 +483,10 @@ static ssize_t bonding_show_active_slave(struct device *d,
|
|||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_active_slave(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ACTIVE_SLAVE, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR,
|
||||
bonding_show_active_slave, bonding_store_active_slave);
|
||||
bonding_show_active_slave, bonding_sysfs_store_option);
|
||||
|
||||
|
||||
/*
|
||||
* Show link status of the bond interface.
|
||||
*/
|
||||
/* Show link status of the bond interface. */
|
||||
static ssize_t bonding_show_mii_status(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -792,9 +497,7 @@ static ssize_t bonding_show_mii_status(struct device *d,
|
|||
}
|
||||
static DEVICE_ATTR(mii_status, S_IRUGO, bonding_show_mii_status, NULL);
|
||||
|
||||
/*
|
||||
* Show current 802.3ad aggregator ID.
|
||||
*/
|
||||
/* Show current 802.3ad aggregator ID. */
|
||||
static ssize_t bonding_show_ad_aggregator(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -802,7 +505,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
|
|||
int count = 0;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info ad_info;
|
||||
count = sprintf(buf, "%d\n",
|
||||
bond_3ad_get_active_agg_info(bond, &ad_info)
|
||||
|
|
@ -814,9 +517,7 @@ static ssize_t bonding_show_ad_aggregator(struct device *d,
|
|||
static DEVICE_ATTR(ad_aggregator, S_IRUGO, bonding_show_ad_aggregator, NULL);
|
||||
|
||||
|
||||
/*
|
||||
* Show number of active 802.3ad ports.
|
||||
*/
|
||||
/* Show number of active 802.3ad ports. */
|
||||
static ssize_t bonding_show_ad_num_ports(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -824,7 +525,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
|
|||
int count = 0;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info ad_info;
|
||||
count = sprintf(buf, "%d\n",
|
||||
bond_3ad_get_active_agg_info(bond, &ad_info)
|
||||
|
|
@ -836,9 +537,7 @@ static ssize_t bonding_show_ad_num_ports(struct device *d,
|
|||
static DEVICE_ATTR(ad_num_ports, S_IRUGO, bonding_show_ad_num_ports, NULL);
|
||||
|
||||
|
||||
/*
|
||||
* Show current 802.3ad actor key.
|
||||
*/
|
||||
/* Show current 802.3ad actor key. */
|
||||
static ssize_t bonding_show_ad_actor_key(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -846,7 +545,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
|
|||
int count = 0;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info ad_info;
|
||||
count = sprintf(buf, "%d\n",
|
||||
bond_3ad_get_active_agg_info(bond, &ad_info)
|
||||
|
|
@ -858,9 +557,7 @@ static ssize_t bonding_show_ad_actor_key(struct device *d,
|
|||
static DEVICE_ATTR(ad_actor_key, S_IRUGO, bonding_show_ad_actor_key, NULL);
|
||||
|
||||
|
||||
/*
|
||||
* Show current 802.3ad partner key.
|
||||
*/
|
||||
/* Show current 802.3ad partner key. */
|
||||
static ssize_t bonding_show_ad_partner_key(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -868,7 +565,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
|
|||
int count = 0;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info ad_info;
|
||||
count = sprintf(buf, "%d\n",
|
||||
bond_3ad_get_active_agg_info(bond, &ad_info)
|
||||
|
|
@ -880,9 +577,7 @@ static ssize_t bonding_show_ad_partner_key(struct device *d,
|
|||
static DEVICE_ATTR(ad_partner_key, S_IRUGO, bonding_show_ad_partner_key, NULL);
|
||||
|
||||
|
||||
/*
|
||||
* Show current 802.3ad partner mac.
|
||||
*/
|
||||
/* Show current 802.3ad partner mac. */
|
||||
static ssize_t bonding_show_ad_partner_mac(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -890,7 +585,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
|
|||
int count = 0;
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
if (bond->params.mode == BOND_MODE_8023AD) {
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
struct ad_info ad_info;
|
||||
if (!bond_3ad_get_active_agg_info(bond, &ad_info))
|
||||
count = sprintf(buf, "%pM\n", ad_info.partner_system);
|
||||
|
|
@ -900,9 +595,7 @@ static ssize_t bonding_show_ad_partner_mac(struct device *d,
|
|||
}
|
||||
static DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
|
||||
|
||||
/*
|
||||
* Show the queue_ids of the slaves in the current bond.
|
||||
*/
|
||||
/* Show the queue_ids of the slaves in the current bond. */
|
||||
static ssize_t bonding_show_queue_id(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -933,31 +626,11 @@ static ssize_t bonding_show_queue_id(struct device *d,
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the queue_ids of the slaves in the current bond. The bond
|
||||
* interface must be enslaved for this to work.
|
||||
*/
|
||||
static ssize_t bonding_store_queue_id(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buffer, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_QUEUE_ID, (char *)buffer);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(queue_id, S_IRUGO | S_IWUSR, bonding_show_queue_id,
|
||||
bonding_store_queue_id);
|
||||
bonding_sysfs_store_option);
|
||||
|
||||
|
||||
/*
|
||||
* Show and set the all_slaves_active flag.
|
||||
*/
|
||||
/* Show the all_slaves_active flag. */
|
||||
static ssize_t bonding_show_slaves_active(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -966,27 +639,10 @@ static ssize_t bonding_show_slaves_active(struct device *d,
|
|||
|
||||
return sprintf(buf, "%d\n", bond->params.all_slaves_active);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_slaves_active(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_ALL_SLAVES_ACTIVE,
|
||||
(char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR,
|
||||
bonding_show_slaves_active, bonding_store_slaves_active);
|
||||
bonding_show_slaves_active, bonding_sysfs_store_option);
|
||||
|
||||
/*
|
||||
* Show and set the number of IGMP membership reports to send on link failure
|
||||
*/
|
||||
/* Show the number of IGMP membership reports to send on link failure */
|
||||
static ssize_t bonding_show_resend_igmp(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -995,23 +651,8 @@ static ssize_t bonding_show_resend_igmp(struct device *d,
|
|||
|
||||
return sprintf(buf, "%d\n", bond->params.resend_igmp);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_resend_igmp(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_RESEND_IGMP, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(resend_igmp, S_IRUGO | S_IWUSR,
|
||||
bonding_show_resend_igmp, bonding_store_resend_igmp);
|
||||
bonding_show_resend_igmp, bonding_sysfs_store_option);
|
||||
|
||||
|
||||
static ssize_t bonding_show_lp_interval(struct device *d,
|
||||
|
|
@ -1019,25 +660,21 @@ static ssize_t bonding_show_lp_interval(struct device *d,
|
|||
char *buf)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
|
||||
return sprintf(buf, "%d\n", bond->params.lp_interval);
|
||||
}
|
||||
static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
|
||||
bonding_show_lp_interval, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_store_lp_interval(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t bonding_show_tlb_dynamic_lb(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_LP_INTERVAL, (char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
return sprintf(buf, "%d\n", bond->params.tlb_dynamic_lb);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR,
|
||||
bonding_show_lp_interval, bonding_store_lp_interval);
|
||||
static DEVICE_ATTR(tlb_dynamic_lb, S_IRUGO | S_IWUSR,
|
||||
bonding_show_tlb_dynamic_lb, bonding_sysfs_store_option);
|
||||
|
||||
static ssize_t bonding_show_packets_per_slave(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
|
|
@ -1045,27 +682,11 @@ static ssize_t bonding_show_packets_per_slave(struct device *d,
|
|||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
unsigned int packets_per_slave = bond->params.packets_per_slave;
|
||||
|
||||
return sprintf(buf, "%u\n", packets_per_slave);
|
||||
}
|
||||
|
||||
static ssize_t bonding_store_packets_per_slave(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct bonding *bond = to_bond(d);
|
||||
int ret;
|
||||
|
||||
ret = bond_opt_tryset_rtnl(bond, BOND_OPT_PACKETS_PER_SLAVE,
|
||||
(char *)buf);
|
||||
if (!ret)
|
||||
ret = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR,
|
||||
bonding_show_packets_per_slave,
|
||||
bonding_store_packets_per_slave);
|
||||
bonding_show_packets_per_slave, bonding_sysfs_store_option);
|
||||
|
||||
static struct attribute *per_bond_attrs[] = {
|
||||
&dev_attr_slaves.attr,
|
||||
|
|
@ -1099,6 +720,7 @@ static struct attribute *per_bond_attrs[] = {
|
|||
&dev_attr_min_links.attr,
|
||||
&dev_attr_lp_interval.attr,
|
||||
&dev_attr_packets_per_slave.attr,
|
||||
&dev_attr_tlb_dynamic_lb.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
@ -1107,8 +729,7 @@ static struct attribute_group bonding_group = {
|
|||
.attrs = per_bond_attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize sysfs. This sets up the bonding_masters file in
|
||||
/* Initialize sysfs. This sets up the bonding_masters file in
|
||||
* /sys/class/net.
|
||||
*/
|
||||
int bond_create_sysfs(struct bond_net *bn)
|
||||
|
|
@ -1120,8 +741,7 @@ int bond_create_sysfs(struct bond_net *bn)
|
|||
|
||||
ret = netdev_class_create_file_ns(&bn->class_attr_bonding_masters,
|
||||
bn->net);
|
||||
/*
|
||||
* Permit multiple loads of the module by ignoring failures to
|
||||
/* Permit multiple loads of the module by ignoring failures to
|
||||
* create the bonding_masters sysfs file. Bonding devices
|
||||
* created by second or subsequent loads of the module will
|
||||
* not be listed in, or controllable by, bonding_masters, but
|
||||
|
|
@ -1144,16 +764,13 @@ int bond_create_sysfs(struct bond_net *bn)
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove /sys/class/net/bonding_masters.
|
||||
*/
|
||||
/* Remove /sys/class/net/bonding_masters. */
|
||||
void bond_destroy_sysfs(struct bond_net *bn)
|
||||
{
|
||||
netdev_class_remove_file_ns(&bn->class_attr_bonding_masters, bn->net);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize sysfs for each bond. This sets up and registers
|
||||
/* Initialize sysfs for each bond. This sets up and registers
|
||||
* the 'bondctl' directory for each individual bond under /sys/class/net.
|
||||
*/
|
||||
void bond_prepare_sysfs_group(struct bonding *bond)
|
||||
|
|
|
|||
|
|
@ -69,8 +69,8 @@ static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf)
|
|||
{
|
||||
const struct aggregator *agg;
|
||||
|
||||
if (slave->bond->params.mode == BOND_MODE_8023AD) {
|
||||
agg = SLAVE_AD_INFO(slave).port.aggregator;
|
||||
if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) {
|
||||
agg = SLAVE_AD_INFO(slave)->port.aggregator;
|
||||
if (agg)
|
||||
return sprintf(buf, "%d\n",
|
||||
agg->aggregator_identifier);
|
||||
|
|
|
|||
|
|
@ -41,42 +41,6 @@
|
|||
|
||||
#define BOND_DEFAULT_MIIMON 100
|
||||
|
||||
#define IS_UP(dev) \
|
||||
((((dev)->flags & IFF_UP) == IFF_UP) && \
|
||||
netif_running(dev) && \
|
||||
netif_carrier_ok(dev))
|
||||
|
||||
/*
|
||||
* Checks whether slave is ready for transmit.
|
||||
*/
|
||||
#define SLAVE_IS_OK(slave) \
|
||||
(((slave)->dev->flags & IFF_UP) && \
|
||||
netif_running((slave)->dev) && \
|
||||
((slave)->link == BOND_LINK_UP) && \
|
||||
bond_is_active_slave(slave))
|
||||
|
||||
|
||||
#define USES_PRIMARY(mode) \
|
||||
(((mode) == BOND_MODE_ACTIVEBACKUP) || \
|
||||
((mode) == BOND_MODE_TLB) || \
|
||||
((mode) == BOND_MODE_ALB))
|
||||
|
||||
#define BOND_NO_USES_ARP(mode) \
|
||||
(((mode) == BOND_MODE_8023AD) || \
|
||||
((mode) == BOND_MODE_TLB) || \
|
||||
((mode) == BOND_MODE_ALB))
|
||||
|
||||
#define TX_QUEUE_OVERRIDE(mode) \
|
||||
(((mode) == BOND_MODE_ACTIVEBACKUP) || \
|
||||
((mode) == BOND_MODE_ROUNDROBIN))
|
||||
|
||||
#define BOND_MODE_IS_LB(mode) \
|
||||
(((mode) == BOND_MODE_TLB) || \
|
||||
((mode) == BOND_MODE_ALB))
|
||||
|
||||
#define IS_IP_TARGET_UNUSABLE_ADDRESS(a) \
|
||||
((htonl(INADDR_BROADCAST) == a) || \
|
||||
ipv4_is_zeronet(a))
|
||||
/*
|
||||
* Less bad way to call ioctl from within the kernel; this needs to be
|
||||
* done some other way to get the call out of interrupt context.
|
||||
|
|
@ -90,6 +54,8 @@
|
|||
set_fs(fs); \
|
||||
res; })
|
||||
|
||||
#define BOND_MODE(bond) ((bond)->params.mode)
|
||||
|
||||
/* slave list primitives */
|
||||
#define bond_slave_list(bond) (&(bond)->dev->adj_list.lower)
|
||||
|
||||
|
|
@ -175,6 +141,7 @@ struct bond_params {
|
|||
int resend_igmp;
|
||||
int lp_interval;
|
||||
int packets_per_slave;
|
||||
int tlb_dynamic_lb;
|
||||
struct reciprocal_value reciprocal_packets_per_slave;
|
||||
};
|
||||
|
||||
|
|
@ -183,8 +150,6 @@ struct bond_parm_tbl {
|
|||
int mode;
|
||||
};
|
||||
|
||||
#define BOND_MAX_MODENAME_LEN 20
|
||||
|
||||
struct slave {
|
||||
struct net_device *dev; /* first - useful for panic debug */
|
||||
struct bonding *bond; /* our master */
|
||||
|
|
@ -205,7 +170,7 @@ struct slave {
|
|||
u32 speed;
|
||||
u16 queue_id;
|
||||
u8 perm_hwaddr[ETH_ALEN];
|
||||
struct ad_slave_info ad_info; /* HUGE - better to dynamically alloc */
|
||||
struct ad_slave_info *ad_info;
|
||||
struct tlb_slave_info tlb_info;
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
struct netpoll *np;
|
||||
|
|
@ -285,14 +250,41 @@ static inline struct slave *bond_get_slave_by_dev(struct bonding *bond,
|
|||
|
||||
static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
|
||||
{
|
||||
if (!slave || !slave->bond)
|
||||
return NULL;
|
||||
return slave->bond;
|
||||
}
|
||||
|
||||
static inline bool bond_should_override_tx_queue(struct bonding *bond)
|
||||
{
|
||||
return BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP ||
|
||||
BOND_MODE(bond) == BOND_MODE_ROUNDROBIN;
|
||||
}
|
||||
|
||||
static inline bool bond_is_lb(const struct bonding *bond)
|
||||
{
|
||||
return BOND_MODE_IS_LB(bond->params.mode);
|
||||
return BOND_MODE(bond) == BOND_MODE_TLB ||
|
||||
BOND_MODE(bond) == BOND_MODE_ALB;
|
||||
}
|
||||
|
||||
static inline bool bond_mode_uses_arp(int mode)
|
||||
{
|
||||
return mode != BOND_MODE_8023AD && mode != BOND_MODE_TLB &&
|
||||
mode != BOND_MODE_ALB;
|
||||
}
|
||||
|
||||
static inline bool bond_mode_uses_primary(int mode)
|
||||
{
|
||||
return mode == BOND_MODE_ACTIVEBACKUP || mode == BOND_MODE_TLB ||
|
||||
mode == BOND_MODE_ALB;
|
||||
}
|
||||
|
||||
static inline bool bond_uses_primary(struct bonding *bond)
|
||||
{
|
||||
return bond_mode_uses_primary(BOND_MODE(bond));
|
||||
}
|
||||
|
||||
static inline bool bond_slave_is_up(struct slave *slave)
|
||||
{
|
||||
return netif_running(slave->dev) && netif_carrier_ok(slave->dev);
|
||||
}
|
||||
|
||||
static inline void bond_set_active_slave(struct slave *slave)
|
||||
|
|
@ -365,6 +357,12 @@ static inline bool bond_is_active_slave(struct slave *slave)
|
|||
return !bond_slave_state(slave);
|
||||
}
|
||||
|
||||
static inline bool bond_slave_can_tx(struct slave *slave)
|
||||
{
|
||||
return bond_slave_is_up(slave) && slave->link == BOND_LINK_UP &&
|
||||
bond_is_active_slave(slave);
|
||||
}
|
||||
|
||||
#define BOND_PRI_RESELECT_ALWAYS 0
|
||||
#define BOND_PRI_RESELECT_BETTER 1
|
||||
#define BOND_PRI_RESELECT_FAILURE 2
|
||||
|
|
@ -396,12 +394,16 @@ static inline int slave_do_arp_validate(struct bonding *bond,
|
|||
return bond->params.arp_validate & (1 << bond_slave_state(slave));
|
||||
}
|
||||
|
||||
static inline int slave_do_arp_validate_only(struct bonding *bond,
|
||||
struct slave *slave)
|
||||
static inline int slave_do_arp_validate_only(struct bonding *bond)
|
||||
{
|
||||
return bond->params.arp_validate & BOND_ARP_FILTER;
|
||||
}
|
||||
|
||||
static inline int bond_is_ip_target_ok(__be32 addr)
|
||||
{
|
||||
return !ipv4_is_lbcast(addr) && !ipv4_is_zeronet(addr);
|
||||
}
|
||||
|
||||
/* Get the oldest arp which we've received on this slave for bond's
|
||||
* arp_targets.
|
||||
*/
|
||||
|
|
@ -479,16 +481,14 @@ static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be3
|
|||
return addr;
|
||||
}
|
||||
|
||||
static inline bool slave_can_tx(struct slave *slave)
|
||||
{
|
||||
if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP &&
|
||||
bond_is_active_slave(slave))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
struct bond_net;
|
||||
struct bond_net {
|
||||
struct net *net; /* Associated network namespace */
|
||||
struct list_head dev_list;
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct proc_dir_entry *proc_dir;
|
||||
#endif
|
||||
struct class_attribute class_attr_bonding_masters;
|
||||
};
|
||||
|
||||
int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave);
|
||||
void bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
|
||||
|
|
@ -500,7 +500,7 @@ int bond_sysfs_slave_add(struct slave *slave);
|
|||
void bond_sysfs_slave_del(struct slave *slave);
|
||||
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev);
|
||||
int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
|
||||
int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count);
|
||||
u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb);
|
||||
void bond_select_active_slave(struct bonding *bond);
|
||||
void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
|
||||
void bond_create_debugfs(void);
|
||||
|
|
@ -516,15 +516,9 @@ void bond_netlink_fini(void);
|
|||
struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond);
|
||||
struct net_device *bond_option_active_slave_get(struct bonding *bond);
|
||||
const char *bond_slave_link_status(s8 link);
|
||||
|
||||
struct bond_net {
|
||||
struct net * net; /* Associated network namespace */
|
||||
struct list_head dev_list;
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct proc_dir_entry * proc_dir;
|
||||
#endif
|
||||
struct class_attribute class_attr_bonding_masters;
|
||||
};
|
||||
bool bond_verify_device_path(struct net_device *start_dev,
|
||||
struct net_device *end_dev,
|
||||
struct bond_vlan_tag *tags);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
void bond_create_proc_entry(struct bonding *bond);
|
||||
|
|
@ -576,6 +570,27 @@ static inline struct slave *bond_slave_has_mac_rcu(struct bonding *bond,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Caller must hold rcu_read_lock() for read */
|
||||
static inline bool bond_slave_has_mac_rx(struct bonding *bond, const u8 *mac)
|
||||
{
|
||||
struct list_head *iter;
|
||||
struct slave *tmp;
|
||||
struct netdev_hw_addr *ha;
|
||||
|
||||
bond_for_each_slave_rcu(bond, tmp, iter)
|
||||
if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr))
|
||||
return true;
|
||||
|
||||
if (netdev_uc_empty(bond->dev))
|
||||
return false;
|
||||
|
||||
netdev_for_each_uc_addr(ha, bond->dev)
|
||||
if (ether_addr_equal_64bits(mac, ha->addr))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the ip is present in arp ip list, or first free slot if ip == 0
|
||||
* Returns -1 if not found, index if found
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ config CAN_LEDS
|
|||
|
||||
config CAN_AT91
|
||||
tristate "Atmel AT91 onchip CAN controller"
|
||||
depends on ARM
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
---help---
|
||||
This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
|
||||
and AT91SAM9X5 processors.
|
||||
|
|
@ -77,12 +77,6 @@ config CAN_TI_HECC
|
|||
Driver for TI HECC (High End CAN Controller) module found on many
|
||||
TI devices. The device specifications are available from www.ti.com
|
||||
|
||||
config CAN_MCP251X
|
||||
tristate "Microchip MCP251x SPI CAN controllers"
|
||||
depends on SPI && HAS_DMA
|
||||
---help---
|
||||
Driver for the Microchip MCP251x SPI CAN controllers.
|
||||
|
||||
config CAN_BFIN
|
||||
depends on BF534 || BF536 || BF537 || BF538 || BF539 || BF54x
|
||||
tristate "Analog Devices Blackfin on-chip CAN"
|
||||
|
|
@ -110,7 +104,7 @@ config CAN_FLEXCAN
|
|||
|
||||
config PCH_CAN
|
||||
tristate "Intel EG20T PCH CAN controller"
|
||||
depends on PCI
|
||||
depends on PCI && (X86_32 || COMPILE_TEST)
|
||||
---help---
|
||||
This driver is for PCH CAN of Topcliff (Intel EG20T PCH) which
|
||||
is an IOH for x86 embedded processor (Intel Atom E6xx series).
|
||||
|
|
@ -125,6 +119,24 @@ config CAN_GRCAN
|
|||
endian syntheses of the cores would need some modifications on
|
||||
the hardware level to work.
|
||||
|
||||
config CAN_RCAR
|
||||
tristate "Renesas R-Car CAN controller"
|
||||
depends on ARM
|
||||
---help---
|
||||
Say Y here if you want to use CAN controller found on Renesas R-Car
|
||||
SoCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called rcar_can.
|
||||
|
||||
config CAN_XILINXCAN
|
||||
tristate "Xilinx CAN"
|
||||
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
|
||||
depends on COMMON_CLK && HAS_IOMEM
|
||||
---help---
|
||||
Xilinx CAN driver. This driver supports both soft AXI CAN IP and
|
||||
Zynq CANPS IP.
|
||||
|
||||
source "drivers/net/can/mscan/Kconfig"
|
||||
|
||||
source "drivers/net/can/sja1000/Kconfig"
|
||||
|
|
@ -133,6 +145,8 @@ source "drivers/net/can/c_can/Kconfig"
|
|||
|
||||
source "drivers/net/can/cc770/Kconfig"
|
||||
|
||||
source "drivers/net/can/spi/Kconfig"
|
||||
|
||||
source "drivers/net/can/usb/Kconfig"
|
||||
|
||||
source "drivers/net/can/softing/Kconfig"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ can-dev-y := dev.o
|
|||
|
||||
can-dev-$(CONFIG_CAN_LEDS) += led.o
|
||||
|
||||
obj-y += spi/
|
||||
obj-y += usb/
|
||||
obj-y += softing/
|
||||
|
||||
|
|
@ -19,11 +20,12 @@ obj-$(CONFIG_CAN_C_CAN) += c_can/
|
|||
obj-$(CONFIG_CAN_CC770) += cc770/
|
||||
obj-$(CONFIG_CAN_AT91) += at91_can.o
|
||||
obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o
|
||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||
obj-$(CONFIG_CAN_BFIN) += bfin_can.o
|
||||
obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o
|
||||
obj-$(CONFIG_CAN_FLEXCAN) += flexcan.o
|
||||
obj-$(CONFIG_PCH_CAN) += pch_can.o
|
||||
obj-$(CONFIG_CAN_GRCAN) += grcan.o
|
||||
obj-$(CONFIG_CAN_RCAR) += rcar_can.o
|
||||
obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
||||
|
|
|
|||
|
|
@ -252,8 +252,7 @@ static void c_can_obj_update(struct net_device *dev, int iface, u32 cmd, u32 obj
|
|||
struct c_can_priv *priv = netdev_priv(dev);
|
||||
int cnt, reg = C_CAN_IFACE(COMREQ_REG, iface);
|
||||
|
||||
priv->write_reg(priv, reg + 1, cmd);
|
||||
priv->write_reg(priv, reg, obj);
|
||||
priv->write_reg32(priv, reg, (cmd << 16) | obj);
|
||||
|
||||
for (cnt = MIN_TIMEOUT_VALUE; cnt; cnt--) {
|
||||
if (!(priv->read_reg(priv, reg) & IF_COMR_BUSY))
|
||||
|
|
@ -328,8 +327,7 @@ static void c_can_setup_tx_object(struct net_device *dev, int iface,
|
|||
change_bit(idx, &priv->tx_dir);
|
||||
}
|
||||
|
||||
priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), arb);
|
||||
priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), arb >> 16);
|
||||
priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), arb);
|
||||
|
||||
priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
|
||||
|
||||
|
|
@ -391,8 +389,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, u32 ctrl)
|
|||
|
||||
frame->can_dlc = get_can_dlc(ctrl & 0x0F);
|
||||
|
||||
arb = priv->read_reg(priv, C_CAN_IFACE(ARB1_REG, iface));
|
||||
arb |= priv->read_reg(priv, C_CAN_IFACE(ARB2_REG, iface)) << 16;
|
||||
arb = priv->read_reg32(priv, C_CAN_IFACE(ARB1_REG, iface));
|
||||
|
||||
if (arb & IF_ARB_MSGXTD)
|
||||
frame->can_id = (arb & CAN_EFF_MASK) | CAN_EFF_FLAG;
|
||||
|
|
@ -424,12 +421,10 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface,
|
|||
struct c_can_priv *priv = netdev_priv(dev);
|
||||
|
||||
mask |= BIT(29);
|
||||
priv->write_reg(priv, C_CAN_IFACE(MASK1_REG, iface), mask);
|
||||
priv->write_reg(priv, C_CAN_IFACE(MASK2_REG, iface), mask >> 16);
|
||||
priv->write_reg32(priv, C_CAN_IFACE(MASK1_REG, iface), mask);
|
||||
|
||||
id |= IF_ARB_MSGVAL;
|
||||
priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), id);
|
||||
priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), id >> 16);
|
||||
priv->write_reg32(priv, C_CAN_IFACE(ARB1_REG, iface), id);
|
||||
|
||||
priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont);
|
||||
c_can_object_put(dev, iface, obj, IF_COMM_RCV_SETUP);
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ enum reg {
|
|||
C_CAN_INTPND2_REG,
|
||||
C_CAN_MSGVAL1_REG,
|
||||
C_CAN_MSGVAL2_REG,
|
||||
C_CAN_FUNCTION_REG,
|
||||
};
|
||||
|
||||
static const u16 reg_map_c_can[] = {
|
||||
|
|
@ -129,6 +130,7 @@ static const u16 reg_map_d_can[] = {
|
|||
[C_CAN_BRPEXT_REG] = 0x0E,
|
||||
[C_CAN_INT_REG] = 0x10,
|
||||
[C_CAN_TEST_REG] = 0x14,
|
||||
[C_CAN_FUNCTION_REG] = 0x18,
|
||||
[C_CAN_TXRQST1_REG] = 0x88,
|
||||
[C_CAN_TXRQST2_REG] = 0x8A,
|
||||
[C_CAN_NEWDAT1_REG] = 0x9C,
|
||||
|
|
@ -176,8 +178,10 @@ struct c_can_priv {
|
|||
atomic_t tx_active;
|
||||
unsigned long tx_dir;
|
||||
int last_status;
|
||||
u16 (*read_reg) (struct c_can_priv *priv, enum reg index);
|
||||
void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val);
|
||||
u16 (*read_reg) (const struct c_can_priv *priv, enum reg index);
|
||||
void (*write_reg) (const struct c_can_priv *priv, enum reg index, u16 val);
|
||||
u32 (*read_reg32) (const struct c_can_priv *priv, enum reg index);
|
||||
void (*write_reg32) (const struct c_can_priv *priv, enum reg index, u32 val);
|
||||
void __iomem *base;
|
||||
const u16 *regs;
|
||||
void *priv; /* for board-specific data */
|
||||
|
|
|
|||
|
|
@ -19,9 +19,13 @@
|
|||
|
||||
#include "c_can.h"
|
||||
|
||||
#define PCI_DEVICE_ID_PCH_CAN 0x8818
|
||||
#define PCH_PCI_SOFT_RESET 0x01fc
|
||||
|
||||
enum c_can_pci_reg_align {
|
||||
C_CAN_REG_ALIGN_16,
|
||||
C_CAN_REG_ALIGN_32,
|
||||
C_CAN_REG_32,
|
||||
};
|
||||
|
||||
struct c_can_pci_data {
|
||||
|
|
@ -31,6 +35,10 @@ struct c_can_pci_data {
|
|||
enum c_can_pci_reg_align reg_align;
|
||||
/* Set the frequency */
|
||||
unsigned int freq;
|
||||
/* PCI bar number */
|
||||
int bar;
|
||||
/* Callback for reset */
|
||||
void (*init)(const struct c_can_priv *priv, bool enable);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -39,30 +47,70 @@ struct c_can_pci_data {
|
|||
* registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
|
||||
* Handle the same by providing a common read/write interface.
|
||||
*/
|
||||
static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv,
|
||||
static u16 c_can_pci_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
|
||||
enum reg index)
|
||||
{
|
||||
return readw(priv->base + priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv,
|
||||
static void c_can_pci_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
|
||||
enum reg index, u16 val)
|
||||
{
|
||||
writew(val, priv->base + priv->regs[index]);
|
||||
}
|
||||
|
||||
static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv,
|
||||
static u16 c_can_pci_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
|
||||
enum reg index)
|
||||
{
|
||||
return readw(priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv,
|
||||
static void c_can_pci_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
|
||||
enum reg index, u16 val)
|
||||
{
|
||||
writew(val, priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static u16 c_can_pci_read_reg_32bit(const struct c_can_priv *priv,
|
||||
enum reg index)
|
||||
{
|
||||
return (u16)ioread32(priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_pci_write_reg_32bit(const struct c_can_priv *priv,
|
||||
enum reg index, u16 val)
|
||||
{
|
||||
iowrite32((u32)val, priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static u32 c_can_pci_read_reg32(const struct c_can_priv *priv, enum reg index)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = priv->read_reg(priv, index);
|
||||
val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void c_can_pci_write_reg32(const struct c_can_priv *priv, enum reg index,
|
||||
u32 val)
|
||||
{
|
||||
priv->write_reg(priv, index + 1, val >> 16);
|
||||
priv->write_reg(priv, index, val);
|
||||
}
|
||||
|
||||
static void c_can_pci_reset_pch(const struct c_can_priv *priv, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
u32 __iomem *addr = priv->base + PCH_PCI_SOFT_RESET;
|
||||
|
||||
/* write to sw reset register */
|
||||
iowrite32(1, addr);
|
||||
iowrite32(0, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static int c_can_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
|
|
@ -90,7 +138,8 @@ static int c_can_pci_probe(struct pci_dev *pdev,
|
|||
pci_set_master(pdev);
|
||||
}
|
||||
|
||||
addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0));
|
||||
addr = pci_iomap(pdev, c_can_pci_data->bar,
|
||||
pci_resource_len(pdev, c_can_pci_data->bar));
|
||||
if (!addr) {
|
||||
dev_err(&pdev->dev,
|
||||
"device has no PCI memory resources, "
|
||||
|
|
@ -147,10 +196,18 @@ static int c_can_pci_probe(struct pci_dev *pdev,
|
|||
priv->read_reg = c_can_pci_read_reg_aligned_to_16bit;
|
||||
priv->write_reg = c_can_pci_write_reg_aligned_to_16bit;
|
||||
break;
|
||||
case C_CAN_REG_32:
|
||||
priv->read_reg = c_can_pci_read_reg_32bit;
|
||||
priv->write_reg = c_can_pci_write_reg_32bit;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out_free_c_can;
|
||||
}
|
||||
priv->read_reg32 = c_can_pci_read_reg32;
|
||||
priv->write_reg32 = c_can_pci_write_reg32;
|
||||
|
||||
priv->raminit = c_can_pci_data->init;
|
||||
|
||||
ret = register_c_can_dev(dev);
|
||||
if (ret) {
|
||||
|
|
@ -198,6 +255,15 @@ static struct c_can_pci_data c_can_sta2x11= {
|
|||
.type = BOSCH_C_CAN,
|
||||
.reg_align = C_CAN_REG_ALIGN_32,
|
||||
.freq = 52000000, /* 52 Mhz */
|
||||
.bar = 0,
|
||||
};
|
||||
|
||||
static struct c_can_pci_data c_can_pch = {
|
||||
.type = BOSCH_C_CAN,
|
||||
.reg_align = C_CAN_REG_32,
|
||||
.freq = 50000000, /* 50 MHz */
|
||||
.init = c_can_pci_reset_pch,
|
||||
.bar = 1,
|
||||
};
|
||||
|
||||
#define C_CAN_ID(_vend, _dev, _driverdata) { \
|
||||
|
|
@ -207,6 +273,8 @@ static struct c_can_pci_data c_can_sta2x11= {
|
|||
static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = {
|
||||
C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN,
|
||||
c_can_sta2x11),
|
||||
C_CAN_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_CAN,
|
||||
c_can_pch),
|
||||
{},
|
||||
};
|
||||
static struct pci_driver c_can_pci_driver = {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#define CAN_RAMINIT_START_MASK(i) (0x001 << (i))
|
||||
#define CAN_RAMINIT_DONE_MASK(i) (0x100 << (i))
|
||||
#define CAN_RAMINIT_ALL_MASK(i) (0x101 << (i))
|
||||
#define DCAN_RAM_INIT_BIT (1 << 3)
|
||||
static DEFINE_SPINLOCK(raminit_lock);
|
||||
/*
|
||||
* 16-bit c_can registers can be arranged differently in the memory
|
||||
|
|
@ -47,31 +48,31 @@ static DEFINE_SPINLOCK(raminit_lock);
|
|||
* registers can be aligned to a 16-bit boundary or 32-bit boundary etc.
|
||||
* Handle the same by providing a common read/write interface.
|
||||
*/
|
||||
static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv,
|
||||
static u16 c_can_plat_read_reg_aligned_to_16bit(const struct c_can_priv *priv,
|
||||
enum reg index)
|
||||
{
|
||||
return readw(priv->base + priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv,
|
||||
static void c_can_plat_write_reg_aligned_to_16bit(const struct c_can_priv *priv,
|
||||
enum reg index, u16 val)
|
||||
{
|
||||
writew(val, priv->base + priv->regs[index]);
|
||||
}
|
||||
|
||||
static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv,
|
||||
static u16 c_can_plat_read_reg_aligned_to_32bit(const struct c_can_priv *priv,
|
||||
enum reg index)
|
||||
{
|
||||
return readw(priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv,
|
||||
static void c_can_plat_write_reg_aligned_to_32bit(const struct c_can_priv *priv,
|
||||
enum reg index, u16 val)
|
||||
{
|
||||
writew(val, priv->base + 2 * priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
|
||||
static void c_can_hw_raminit_wait_ti(const struct c_can_priv *priv, u32 mask,
|
||||
u32 val)
|
||||
{
|
||||
/* We look only at the bits of our instance. */
|
||||
|
|
@ -80,7 +81,7 @@ static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask,
|
|||
udelay(1);
|
||||
}
|
||||
|
||||
static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
|
||||
static void c_can_hw_raminit_ti(const struct c_can_priv *priv, bool enable)
|
||||
{
|
||||
u32 mask = CAN_RAMINIT_ALL_MASK(priv->instance);
|
||||
u32 ctrl;
|
||||
|
|
@ -96,18 +97,68 @@ static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
|
|||
ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
|
||||
writel(ctrl, priv->raminit_ctrlreg);
|
||||
ctrl &= ~CAN_RAMINIT_DONE_MASK(priv->instance);
|
||||
c_can_hw_raminit_wait(priv, ctrl, mask);
|
||||
c_can_hw_raminit_wait_ti(priv, ctrl, mask);
|
||||
|
||||
if (enable) {
|
||||
/* Set start bit and wait for the done bit. */
|
||||
ctrl |= CAN_RAMINIT_START_MASK(priv->instance);
|
||||
writel(ctrl, priv->raminit_ctrlreg);
|
||||
ctrl |= CAN_RAMINIT_DONE_MASK(priv->instance);
|
||||
c_can_hw_raminit_wait(priv, ctrl, mask);
|
||||
c_can_hw_raminit_wait_ti(priv, ctrl, mask);
|
||||
}
|
||||
spin_unlock(&raminit_lock);
|
||||
}
|
||||
|
||||
static u32 c_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = priv->read_reg(priv, index);
|
||||
val |= ((u32) priv->read_reg(priv, index + 1)) << 16;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void c_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
|
||||
u32 val)
|
||||
{
|
||||
priv->write_reg(priv, index + 1, val >> 16);
|
||||
priv->write_reg(priv, index, val);
|
||||
}
|
||||
|
||||
static u32 d_can_plat_read_reg32(const struct c_can_priv *priv, enum reg index)
|
||||
{
|
||||
return readl(priv->base + priv->regs[index]);
|
||||
}
|
||||
|
||||
static void d_can_plat_write_reg32(const struct c_can_priv *priv, enum reg index,
|
||||
u32 val)
|
||||
{
|
||||
writel(val, priv->base + priv->regs[index]);
|
||||
}
|
||||
|
||||
static void c_can_hw_raminit_wait(const struct c_can_priv *priv, u32 mask)
|
||||
{
|
||||
while (priv->read_reg32(priv, C_CAN_FUNCTION_REG) & mask)
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = priv->read_reg32(priv, C_CAN_FUNCTION_REG);
|
||||
ctrl &= ~DCAN_RAM_INIT_BIT;
|
||||
priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
|
||||
c_can_hw_raminit_wait(priv, ctrl);
|
||||
|
||||
if (enable) {
|
||||
ctrl |= DCAN_RAM_INIT_BIT;
|
||||
priv->write_reg32(priv, C_CAN_FUNCTION_REG, ctrl);
|
||||
c_can_hw_raminit_wait(priv, ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
static struct platform_device_id c_can_id_table[] = {
|
||||
[BOSCH_C_CAN_PLATFORM] = {
|
||||
.name = KBUILD_MODNAME,
|
||||
|
|
@ -201,11 +252,15 @@ static int c_can_plat_probe(struct platform_device *pdev)
|
|||
case IORESOURCE_MEM_32BIT:
|
||||
priv->read_reg = c_can_plat_read_reg_aligned_to_32bit;
|
||||
priv->write_reg = c_can_plat_write_reg_aligned_to_32bit;
|
||||
priv->read_reg32 = c_can_plat_read_reg32;
|
||||
priv->write_reg32 = c_can_plat_write_reg32;
|
||||
break;
|
||||
case IORESOURCE_MEM_16BIT:
|
||||
default:
|
||||
priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
|
||||
priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
|
||||
priv->read_reg32 = c_can_plat_read_reg32;
|
||||
priv->write_reg32 = c_can_plat_write_reg32;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
@ -214,6 +269,8 @@ static int c_can_plat_probe(struct platform_device *pdev)
|
|||
priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
|
||||
priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
|
||||
priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
|
||||
priv->read_reg32 = d_can_plat_read_reg32;
|
||||
priv->write_reg32 = d_can_plat_write_reg32;
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can");
|
||||
|
|
@ -221,11 +278,20 @@ static int c_can_plat_probe(struct platform_device *pdev)
|
|||
priv->instance = pdev->id;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
/* Not all D_CAN modules have a separate register for the D_CAN
|
||||
* RAM initialization. Use default RAM init bit in D_CAN module
|
||||
* if not specified in DT.
|
||||
*/
|
||||
if (!res) {
|
||||
priv->raminit = c_can_hw_raminit;
|
||||
break;
|
||||
}
|
||||
|
||||
priv->raminit_ctrlreg = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->raminit_ctrlreg) || priv->instance < 0)
|
||||
dev_info(&pdev->dev, "control memory is not used for raminit\n");
|
||||
else
|
||||
priv->raminit = c_can_hw_raminit;
|
||||
priv->raminit = c_can_hw_raminit_ti;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
config CAN_MSCAN
|
||||
depends on PPC || M68K
|
||||
depends on PPC
|
||||
tristate "Support for Freescale MSCAN based chips"
|
||||
---help---
|
||||
The Motorola Scalable Controller Area Network (MSCAN) definition
|
||||
|
|
|
|||
876
drivers/net/can/rcar_can.c
Normal file
876
drivers/net/can/rcar_can.c
Normal file
|
|
@ -0,0 +1,876 @@
|
|||
/* Renesas R-Car CAN device driver
|
||||
*
|
||||
* Copyright (C) 2013 Cogent Embedded, Inc. <source@cogentembedded.com>
|
||||
* Copyright (C) 2013 Renesas Solutions Corp.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/can/led.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/can/platform/rcar_can.h>
|
||||
|
||||
#define RCAR_CAN_DRV_NAME "rcar_can"
|
||||
|
||||
/* Mailbox configuration:
|
||||
* mailbox 60 - 63 - Rx FIFO mailboxes
|
||||
* mailbox 56 - 59 - Tx FIFO mailboxes
|
||||
* non-FIFO mailboxes are not used
|
||||
*/
|
||||
#define RCAR_CAN_N_MBX 64 /* Number of mailboxes in non-FIFO mode */
|
||||
#define RCAR_CAN_RX_FIFO_MBX 60 /* Mailbox - window to Rx FIFO */
|
||||
#define RCAR_CAN_TX_FIFO_MBX 56 /* Mailbox - window to Tx FIFO */
|
||||
#define RCAR_CAN_FIFO_DEPTH 4
|
||||
|
||||
/* Mailbox registers structure */
|
||||
struct rcar_can_mbox_regs {
|
||||
u32 id; /* IDE and RTR bits, SID and EID */
|
||||
u8 stub; /* Not used */
|
||||
u8 dlc; /* Data Length Code - bits [0..3] */
|
||||
u8 data[8]; /* Data Bytes */
|
||||
u8 tsh; /* Time Stamp Higher Byte */
|
||||
u8 tsl; /* Time Stamp Lower Byte */
|
||||
};
|
||||
|
||||
struct rcar_can_regs {
|
||||
struct rcar_can_mbox_regs mb[RCAR_CAN_N_MBX]; /* Mailbox registers */
|
||||
u32 mkr_2_9[8]; /* Mask Registers 2-9 */
|
||||
u32 fidcr[2]; /* FIFO Received ID Compare Register */
|
||||
u32 mkivlr1; /* Mask Invalid Register 1 */
|
||||
u32 mier1; /* Mailbox Interrupt Enable Register 1 */
|
||||
u32 mkr_0_1[2]; /* Mask Registers 0-1 */
|
||||
u32 mkivlr0; /* Mask Invalid Register 0*/
|
||||
u32 mier0; /* Mailbox Interrupt Enable Register 0 */
|
||||
u8 pad_440[0x3c0];
|
||||
u8 mctl[64]; /* Message Control Registers */
|
||||
u16 ctlr; /* Control Register */
|
||||
u16 str; /* Status register */
|
||||
u8 bcr[3]; /* Bit Configuration Register */
|
||||
u8 clkr; /* Clock Select Register */
|
||||
u8 rfcr; /* Receive FIFO Control Register */
|
||||
u8 rfpcr; /* Receive FIFO Pointer Control Register */
|
||||
u8 tfcr; /* Transmit FIFO Control Register */
|
||||
u8 tfpcr; /* Transmit FIFO Pointer Control Register */
|
||||
u8 eier; /* Error Interrupt Enable Register */
|
||||
u8 eifr; /* Error Interrupt Factor Judge Register */
|
||||
u8 recr; /* Receive Error Count Register */
|
||||
u8 tecr; /* Transmit Error Count Register */
|
||||
u8 ecsr; /* Error Code Store Register */
|
||||
u8 cssr; /* Channel Search Support Register */
|
||||
u8 mssr; /* Mailbox Search Status Register */
|
||||
u8 msmr; /* Mailbox Search Mode Register */
|
||||
u16 tsr; /* Time Stamp Register */
|
||||
u8 afsr; /* Acceptance Filter Support Register */
|
||||
u8 pad_857;
|
||||
u8 tcr; /* Test Control Register */
|
||||
u8 pad_859[7];
|
||||
u8 ier; /* Interrupt Enable Register */
|
||||
u8 isr; /* Interrupt Status Register */
|
||||
u8 pad_862;
|
||||
u8 mbsmr; /* Mailbox Search Mask Register */
|
||||
};
|
||||
|
||||
struct rcar_can_priv {
|
||||
struct can_priv can; /* Must be the first member! */
|
||||
struct net_device *ndev;
|
||||
struct napi_struct napi;
|
||||
struct rcar_can_regs __iomem *regs;
|
||||
struct clk *clk;
|
||||
u8 tx_dlc[RCAR_CAN_FIFO_DEPTH];
|
||||
u32 tx_head;
|
||||
u32 tx_tail;
|
||||
u8 clock_select;
|
||||
u8 ier;
|
||||
};
|
||||
|
||||
static const struct can_bittiming_const rcar_can_bittiming_const = {
|
||||
.name = RCAR_CAN_DRV_NAME,
|
||||
.tseg1_min = 4,
|
||||
.tseg1_max = 16,
|
||||
.tseg2_min = 2,
|
||||
.tseg2_max = 8,
|
||||
.sjw_max = 4,
|
||||
.brp_min = 1,
|
||||
.brp_max = 1024,
|
||||
.brp_inc = 1,
|
||||
};
|
||||
|
||||
/* Control Register bits */
|
||||
#define RCAR_CAN_CTLR_BOM (3 << 11) /* Bus-Off Recovery Mode Bits */
|
||||
#define RCAR_CAN_CTLR_BOM_ENT (1 << 11) /* Entry to halt mode */
|
||||
/* at bus-off entry */
|
||||
#define RCAR_CAN_CTLR_SLPM (1 << 10)
|
||||
#define RCAR_CAN_CTLR_CANM (3 << 8) /* Operating Mode Select Bit */
|
||||
#define RCAR_CAN_CTLR_CANM_HALT (1 << 9)
|
||||
#define RCAR_CAN_CTLR_CANM_RESET (1 << 8)
|
||||
#define RCAR_CAN_CTLR_CANM_FORCE_RESET (3 << 8)
|
||||
#define RCAR_CAN_CTLR_MLM (1 << 3) /* Message Lost Mode Select */
|
||||
#define RCAR_CAN_CTLR_IDFM (3 << 1) /* ID Format Mode Select Bits */
|
||||
#define RCAR_CAN_CTLR_IDFM_MIXED (1 << 2) /* Mixed ID mode */
|
||||
#define RCAR_CAN_CTLR_MBM (1 << 0) /* Mailbox Mode select */
|
||||
|
||||
/* Status Register bits */
|
||||
#define RCAR_CAN_STR_RSTST (1 << 8) /* Reset Status Bit */
|
||||
|
||||
/* FIFO Received ID Compare Registers 0 and 1 bits */
|
||||
#define RCAR_CAN_FIDCR_IDE (1 << 31) /* ID Extension Bit */
|
||||
#define RCAR_CAN_FIDCR_RTR (1 << 30) /* Remote Transmission Request Bit */
|
||||
|
||||
/* Receive FIFO Control Register bits */
|
||||
#define RCAR_CAN_RFCR_RFEST (1 << 7) /* Receive FIFO Empty Status Flag */
|
||||
#define RCAR_CAN_RFCR_RFE (1 << 0) /* Receive FIFO Enable */
|
||||
|
||||
/* Transmit FIFO Control Register bits */
|
||||
#define RCAR_CAN_TFCR_TFUST (7 << 1) /* Transmit FIFO Unsent Message */
|
||||
/* Number Status Bits */
|
||||
#define RCAR_CAN_TFCR_TFUST_SHIFT 1 /* Offset of Transmit FIFO Unsent */
|
||||
/* Message Number Status Bits */
|
||||
#define RCAR_CAN_TFCR_TFE (1 << 0) /* Transmit FIFO Enable */
|
||||
|
||||
#define RCAR_CAN_N_RX_MKREGS1 2 /* Number of mask registers */
|
||||
/* for Rx mailboxes 0-31 */
|
||||
#define RCAR_CAN_N_RX_MKREGS2 8
|
||||
|
||||
/* Bit Configuration Register settings */
|
||||
#define RCAR_CAN_BCR_TSEG1(x) (((x) & 0x0f) << 20)
|
||||
#define RCAR_CAN_BCR_BPR(x) (((x) & 0x3ff) << 8)
|
||||
#define RCAR_CAN_BCR_SJW(x) (((x) & 0x3) << 4)
|
||||
#define RCAR_CAN_BCR_TSEG2(x) ((x) & 0x07)
|
||||
|
||||
/* Mailbox and Mask Registers bits */
|
||||
#define RCAR_CAN_IDE (1 << 31)
|
||||
#define RCAR_CAN_RTR (1 << 30)
|
||||
#define RCAR_CAN_SID_SHIFT 18
|
||||
|
||||
/* Mailbox Interrupt Enable Register 1 bits */
|
||||
#define RCAR_CAN_MIER1_RXFIE (1 << 28) /* Receive FIFO Interrupt Enable */
|
||||
#define RCAR_CAN_MIER1_TXFIE (1 << 24) /* Transmit FIFO Interrupt Enable */
|
||||
|
||||
/* Interrupt Enable Register bits */
|
||||
#define RCAR_CAN_IER_ERSIE (1 << 5) /* Error (ERS) Interrupt Enable Bit */
|
||||
#define RCAR_CAN_IER_RXFIE (1 << 4) /* Reception FIFO Interrupt */
|
||||
/* Enable Bit */
|
||||
#define RCAR_CAN_IER_TXFIE (1 << 3) /* Transmission FIFO Interrupt */
|
||||
/* Enable Bit */
|
||||
/* Interrupt Status Register bits */
|
||||
#define RCAR_CAN_ISR_ERSF (1 << 5) /* Error (ERS) Interrupt Status Bit */
|
||||
#define RCAR_CAN_ISR_RXFF (1 << 4) /* Reception FIFO Interrupt */
|
||||
/* Status Bit */
|
||||
#define RCAR_CAN_ISR_TXFF (1 << 3) /* Transmission FIFO Interrupt */
|
||||
/* Status Bit */
|
||||
|
||||
/* Error Interrupt Enable Register bits */
|
||||
#define RCAR_CAN_EIER_BLIE (1 << 7) /* Bus Lock Interrupt Enable */
|
||||
#define RCAR_CAN_EIER_OLIE (1 << 6) /* Overload Frame Transmit */
|
||||
/* Interrupt Enable */
|
||||
#define RCAR_CAN_EIER_ORIE (1 << 5) /* Receive Overrun Interrupt Enable */
|
||||
#define RCAR_CAN_EIER_BORIE (1 << 4) /* Bus-Off Recovery Interrupt Enable */
|
||||
#define RCAR_CAN_EIER_BOEIE (1 << 3) /* Bus-Off Entry Interrupt Enable */
|
||||
#define RCAR_CAN_EIER_EPIE (1 << 2) /* Error Passive Interrupt Enable */
|
||||
#define RCAR_CAN_EIER_EWIE (1 << 1) /* Error Warning Interrupt Enable */
|
||||
#define RCAR_CAN_EIER_BEIE (1 << 0) /* Bus Error Interrupt Enable */
|
||||
|
||||
/* Error Interrupt Factor Judge Register bits */
|
||||
#define RCAR_CAN_EIFR_BLIF (1 << 7) /* Bus Lock Detect Flag */
|
||||
#define RCAR_CAN_EIFR_OLIF (1 << 6) /* Overload Frame Transmission */
|
||||
/* Detect Flag */
|
||||
#define RCAR_CAN_EIFR_ORIF (1 << 5) /* Receive Overrun Detect Flag */
|
||||
#define RCAR_CAN_EIFR_BORIF (1 << 4) /* Bus-Off Recovery Detect Flag */
|
||||
#define RCAR_CAN_EIFR_BOEIF (1 << 3) /* Bus-Off Entry Detect Flag */
|
||||
#define RCAR_CAN_EIFR_EPIF (1 << 2) /* Error Passive Detect Flag */
|
||||
#define RCAR_CAN_EIFR_EWIF (1 << 1) /* Error Warning Detect Flag */
|
||||
#define RCAR_CAN_EIFR_BEIF (1 << 0) /* Bus Error Detect Flag */
|
||||
|
||||
/* Error Code Store Register bits */
|
||||
#define RCAR_CAN_ECSR_EDPM (1 << 7) /* Error Display Mode Select Bit */
|
||||
#define RCAR_CAN_ECSR_ADEF (1 << 6) /* ACK Delimiter Error Flag */
|
||||
#define RCAR_CAN_ECSR_BE0F (1 << 5) /* Bit Error (dominant) Flag */
|
||||
#define RCAR_CAN_ECSR_BE1F (1 << 4) /* Bit Error (recessive) Flag */
|
||||
#define RCAR_CAN_ECSR_CEF (1 << 3) /* CRC Error Flag */
|
||||
#define RCAR_CAN_ECSR_AEF (1 << 2) /* ACK Error Flag */
|
||||
#define RCAR_CAN_ECSR_FEF (1 << 1) /* Form Error Flag */
|
||||
#define RCAR_CAN_ECSR_SEF (1 << 0) /* Stuff Error Flag */
|
||||
|
||||
#define RCAR_CAN_NAPI_WEIGHT 4
|
||||
#define MAX_STR_READS 0x100
|
||||
|
||||
static void tx_failure_cleanup(struct net_device *ndev)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < RCAR_CAN_FIFO_DEPTH; i++)
|
||||
can_free_echo_skb(ndev, i);
|
||||
}
|
||||
|
||||
static void rcar_can_error(struct net_device *ndev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
struct net_device_stats *stats = &ndev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
u8 eifr, txerr = 0, rxerr = 0;
|
||||
|
||||
/* Propagate the error condition to the CAN stack */
|
||||
skb = alloc_can_err_skb(ndev, &cf);
|
||||
|
||||
eifr = readb(&priv->regs->eifr);
|
||||
if (eifr & (RCAR_CAN_EIFR_EWIF | RCAR_CAN_EIFR_EPIF)) {
|
||||
txerr = readb(&priv->regs->tecr);
|
||||
rxerr = readb(&priv->regs->recr);
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[6] = txerr;
|
||||
cf->data[7] = rxerr;
|
||||
}
|
||||
}
|
||||
if (eifr & RCAR_CAN_EIFR_BEIF) {
|
||||
int rx_errors = 0, tx_errors = 0;
|
||||
u8 ecsr;
|
||||
|
||||
netdev_dbg(priv->ndev, "Bus error interrupt:\n");
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
|
||||
cf->data[2] = CAN_ERR_PROT_UNSPEC;
|
||||
}
|
||||
ecsr = readb(&priv->regs->ecsr);
|
||||
if (ecsr & RCAR_CAN_ECSR_ADEF) {
|
||||
netdev_dbg(priv->ndev, "ACK Delimiter Error\n");
|
||||
tx_errors++;
|
||||
writeb(~RCAR_CAN_ECSR_ADEF, &priv->regs->ecsr);
|
||||
if (skb)
|
||||
cf->data[3] |= CAN_ERR_PROT_LOC_ACK_DEL;
|
||||
}
|
||||
if (ecsr & RCAR_CAN_ECSR_BE0F) {
|
||||
netdev_dbg(priv->ndev, "Bit Error (dominant)\n");
|
||||
tx_errors++;
|
||||
writeb(~RCAR_CAN_ECSR_BE0F, &priv->regs->ecsr);
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT0;
|
||||
}
|
||||
if (ecsr & RCAR_CAN_ECSR_BE1F) {
|
||||
netdev_dbg(priv->ndev, "Bit Error (recessive)\n");
|
||||
tx_errors++;
|
||||
writeb(~RCAR_CAN_ECSR_BE1F, &priv->regs->ecsr);
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT1;
|
||||
}
|
||||
if (ecsr & RCAR_CAN_ECSR_CEF) {
|
||||
netdev_dbg(priv->ndev, "CRC Error\n");
|
||||
rx_errors++;
|
||||
writeb(~RCAR_CAN_ECSR_CEF, &priv->regs->ecsr);
|
||||
if (skb)
|
||||
cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
|
||||
}
|
||||
if (ecsr & RCAR_CAN_ECSR_AEF) {
|
||||
netdev_dbg(priv->ndev, "ACK Error\n");
|
||||
tx_errors++;
|
||||
writeb(~RCAR_CAN_ECSR_AEF, &priv->regs->ecsr);
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_ACK;
|
||||
cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
|
||||
}
|
||||
}
|
||||
if (ecsr & RCAR_CAN_ECSR_FEF) {
|
||||
netdev_dbg(priv->ndev, "Form Error\n");
|
||||
rx_errors++;
|
||||
writeb(~RCAR_CAN_ECSR_FEF, &priv->regs->ecsr);
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_FORM;
|
||||
}
|
||||
if (ecsr & RCAR_CAN_ECSR_SEF) {
|
||||
netdev_dbg(priv->ndev, "Stuff Error\n");
|
||||
rx_errors++;
|
||||
writeb(~RCAR_CAN_ECSR_SEF, &priv->regs->ecsr);
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_STUFF;
|
||||
}
|
||||
|
||||
priv->can.can_stats.bus_error++;
|
||||
ndev->stats.rx_errors += rx_errors;
|
||||
ndev->stats.tx_errors += tx_errors;
|
||||
writeb(~RCAR_CAN_EIFR_BEIF, &priv->regs->eifr);
|
||||
}
|
||||
if (eifr & RCAR_CAN_EIFR_EWIF) {
|
||||
netdev_dbg(priv->ndev, "Error warning interrupt\n");
|
||||
priv->can.state = CAN_STATE_ERROR_WARNING;
|
||||
priv->can.can_stats.error_warning++;
|
||||
/* Clear interrupt condition */
|
||||
writeb(~RCAR_CAN_EIFR_EWIF, &priv->regs->eifr);
|
||||
if (skb)
|
||||
cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_WARNING :
|
||||
CAN_ERR_CRTL_RX_WARNING;
|
||||
}
|
||||
if (eifr & RCAR_CAN_EIFR_EPIF) {
|
||||
netdev_dbg(priv->ndev, "Error passive interrupt\n");
|
||||
priv->can.state = CAN_STATE_ERROR_PASSIVE;
|
||||
priv->can.can_stats.error_passive++;
|
||||
/* Clear interrupt condition */
|
||||
writeb(~RCAR_CAN_EIFR_EPIF, &priv->regs->eifr);
|
||||
if (skb)
|
||||
cf->data[1] = txerr > rxerr ? CAN_ERR_CRTL_TX_PASSIVE :
|
||||
CAN_ERR_CRTL_RX_PASSIVE;
|
||||
}
|
||||
if (eifr & RCAR_CAN_EIFR_BOEIF) {
|
||||
netdev_dbg(priv->ndev, "Bus-off entry interrupt\n");
|
||||
tx_failure_cleanup(ndev);
|
||||
priv->ier = RCAR_CAN_IER_ERSIE;
|
||||
writeb(priv->ier, &priv->regs->ier);
|
||||
priv->can.state = CAN_STATE_BUS_OFF;
|
||||
/* Clear interrupt condition */
|
||||
writeb(~RCAR_CAN_EIFR_BOEIF, &priv->regs->eifr);
|
||||
can_bus_off(ndev);
|
||||
if (skb)
|
||||
cf->can_id |= CAN_ERR_BUSOFF;
|
||||
}
|
||||
if (eifr & RCAR_CAN_EIFR_ORIF) {
|
||||
netdev_dbg(priv->ndev, "Receive overrun error interrupt\n");
|
||||
ndev->stats.rx_over_errors++;
|
||||
ndev->stats.rx_errors++;
|
||||
writeb(~RCAR_CAN_EIFR_ORIF, &priv->regs->eifr);
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
}
|
||||
}
|
||||
if (eifr & RCAR_CAN_EIFR_OLIF) {
|
||||
netdev_dbg(priv->ndev,
|
||||
"Overload Frame Transmission error interrupt\n");
|
||||
ndev->stats.rx_over_errors++;
|
||||
ndev->stats.rx_errors++;
|
||||
writeb(~RCAR_CAN_EIFR_OLIF, &priv->regs->eifr);
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_PROT;
|
||||
cf->data[2] |= CAN_ERR_PROT_OVERLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
if (skb) {
|
||||
stats->rx_packets++;
|
||||
stats->rx_bytes += cf->can_dlc;
|
||||
netif_rx(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void rcar_can_tx_done(struct net_device *ndev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
struct net_device_stats *stats = &ndev->stats;
|
||||
u8 isr;
|
||||
|
||||
while (1) {
|
||||
u8 unsent = readb(&priv->regs->tfcr);
|
||||
|
||||
unsent = (unsent & RCAR_CAN_TFCR_TFUST) >>
|
||||
RCAR_CAN_TFCR_TFUST_SHIFT;
|
||||
if (priv->tx_head - priv->tx_tail <= unsent)
|
||||
break;
|
||||
stats->tx_packets++;
|
||||
stats->tx_bytes += priv->tx_dlc[priv->tx_tail %
|
||||
RCAR_CAN_FIFO_DEPTH];
|
||||
priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0;
|
||||
can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH);
|
||||
priv->tx_tail++;
|
||||
netif_wake_queue(ndev);
|
||||
}
|
||||
/* Clear interrupt */
|
||||
isr = readb(&priv->regs->isr);
|
||||
writeb(isr & ~RCAR_CAN_ISR_TXFF, &priv->regs->isr);
|
||||
can_led_event(ndev, CAN_LED_EVENT_TX);
|
||||
}
|
||||
|
||||
static irqreturn_t rcar_can_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct net_device *ndev = dev_id;
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
u8 isr;
|
||||
|
||||
isr = readb(&priv->regs->isr);
|
||||
if (!(isr & priv->ier))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (isr & RCAR_CAN_ISR_ERSF)
|
||||
rcar_can_error(ndev);
|
||||
|
||||
if (isr & RCAR_CAN_ISR_TXFF)
|
||||
rcar_can_tx_done(ndev);
|
||||
|
||||
if (isr & RCAR_CAN_ISR_RXFF) {
|
||||
if (napi_schedule_prep(&priv->napi)) {
|
||||
/* Disable Rx FIFO interrupts */
|
||||
priv->ier &= ~RCAR_CAN_IER_RXFIE;
|
||||
writeb(priv->ier, &priv->regs->ier);
|
||||
__napi_schedule(&priv->napi);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void rcar_can_set_bittiming(struct net_device *dev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(dev);
|
||||
struct can_bittiming *bt = &priv->can.bittiming;
|
||||
u32 bcr;
|
||||
|
||||
bcr = RCAR_CAN_BCR_TSEG1(bt->phase_seg1 + bt->prop_seg - 1) |
|
||||
RCAR_CAN_BCR_BPR(bt->brp - 1) | RCAR_CAN_BCR_SJW(bt->sjw - 1) |
|
||||
RCAR_CAN_BCR_TSEG2(bt->phase_seg2 - 1);
|
||||
/* Don't overwrite CLKR with 32-bit BCR access; CLKR has 8-bit access.
|
||||
* All the registers are big-endian but they get byte-swapped on 32-bit
|
||||
* read/write (but not on 8-bit, contrary to the manuals)...
|
||||
*/
|
||||
writel((bcr << 8) | priv->clock_select, &priv->regs->bcr);
|
||||
}
|
||||
|
||||
static void rcar_can_start(struct net_device *ndev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
u16 ctlr;
|
||||
int i;
|
||||
|
||||
/* Set controller to known mode:
|
||||
* - FIFO mailbox mode
|
||||
* - accept all messages
|
||||
* - overrun mode
|
||||
* CAN is in sleep mode after MCU hardware or software reset.
|
||||
*/
|
||||
ctlr = readw(&priv->regs->ctlr);
|
||||
ctlr &= ~RCAR_CAN_CTLR_SLPM;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
/* Go to reset mode */
|
||||
ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
for (i = 0; i < MAX_STR_READS; i++) {
|
||||
if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)
|
||||
break;
|
||||
}
|
||||
rcar_can_set_bittiming(ndev);
|
||||
ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */
|
||||
ctlr |= RCAR_CAN_CTLR_BOM_ENT; /* Entry to halt mode automatically */
|
||||
/* at bus-off */
|
||||
ctlr |= RCAR_CAN_CTLR_MBM; /* Select FIFO mailbox mode */
|
||||
ctlr |= RCAR_CAN_CTLR_MLM; /* Overrun mode */
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
|
||||
/* Accept all SID and EID */
|
||||
writel(0, &priv->regs->mkr_2_9[6]);
|
||||
writel(0, &priv->regs->mkr_2_9[7]);
|
||||
/* In FIFO mailbox mode, write "0" to bits 24 to 31 */
|
||||
writel(0, &priv->regs->mkivlr1);
|
||||
/* Accept all frames */
|
||||
writel(0, &priv->regs->fidcr[0]);
|
||||
writel(RCAR_CAN_FIDCR_IDE | RCAR_CAN_FIDCR_RTR, &priv->regs->fidcr[1]);
|
||||
/* Enable and configure FIFO mailbox interrupts */
|
||||
writel(RCAR_CAN_MIER1_RXFIE | RCAR_CAN_MIER1_TXFIE, &priv->regs->mier1);
|
||||
|
||||
priv->ier = RCAR_CAN_IER_ERSIE | RCAR_CAN_IER_RXFIE |
|
||||
RCAR_CAN_IER_TXFIE;
|
||||
writeb(priv->ier, &priv->regs->ier);
|
||||
|
||||
/* Accumulate error codes */
|
||||
writeb(RCAR_CAN_ECSR_EDPM, &priv->regs->ecsr);
|
||||
/* Enable error interrupts */
|
||||
writeb(RCAR_CAN_EIER_EWIE | RCAR_CAN_EIER_EPIE | RCAR_CAN_EIER_BOEIE |
|
||||
(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING ?
|
||||
RCAR_CAN_EIER_BEIE : 0) | RCAR_CAN_EIER_ORIE |
|
||||
RCAR_CAN_EIER_OLIE, &priv->regs->eier);
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
/* Go to operation mode */
|
||||
writew(ctlr & ~RCAR_CAN_CTLR_CANM, &priv->regs->ctlr);
|
||||
for (i = 0; i < MAX_STR_READS; i++) {
|
||||
if (!(readw(&priv->regs->str) & RCAR_CAN_STR_RSTST))
|
||||
break;
|
||||
}
|
||||
/* Enable Rx and Tx FIFO */
|
||||
writeb(RCAR_CAN_RFCR_RFE, &priv->regs->rfcr);
|
||||
writeb(RCAR_CAN_TFCR_TFE, &priv->regs->tfcr);
|
||||
}
|
||||
|
||||
static int rcar_can_open(struct net_device *ndev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk);
|
||||
if (err) {
|
||||
netdev_err(ndev, "clk_prepare_enable() failed, error %d\n",
|
||||
err);
|
||||
goto out;
|
||||
}
|
||||
err = open_candev(ndev);
|
||||
if (err) {
|
||||
netdev_err(ndev, "open_candev() failed, error %d\n", err);
|
||||
goto out_clock;
|
||||
}
|
||||
napi_enable(&priv->napi);
|
||||
err = request_irq(ndev->irq, rcar_can_interrupt, 0, ndev->name, ndev);
|
||||
if (err) {
|
||||
netdev_err(ndev, "error requesting interrupt %x\n", ndev->irq);
|
||||
goto out_close;
|
||||
}
|
||||
can_led_event(ndev, CAN_LED_EVENT_OPEN);
|
||||
rcar_can_start(ndev);
|
||||
netif_start_queue(ndev);
|
||||
return 0;
|
||||
out_close:
|
||||
napi_disable(&priv->napi);
|
||||
close_candev(ndev);
|
||||
out_clock:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rcar_can_stop(struct net_device *ndev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
u16 ctlr;
|
||||
int i;
|
||||
|
||||
/* Go to (force) reset mode */
|
||||
ctlr = readw(&priv->regs->ctlr);
|
||||
ctlr |= RCAR_CAN_CTLR_CANM_FORCE_RESET;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
for (i = 0; i < MAX_STR_READS; i++) {
|
||||
if (readw(&priv->regs->str) & RCAR_CAN_STR_RSTST)
|
||||
break;
|
||||
}
|
||||
writel(0, &priv->regs->mier0);
|
||||
writel(0, &priv->regs->mier1);
|
||||
writeb(0, &priv->regs->ier);
|
||||
writeb(0, &priv->regs->eier);
|
||||
/* Go to sleep mode */
|
||||
ctlr |= RCAR_CAN_CTLR_SLPM;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
priv->can.state = CAN_STATE_STOPPED;
|
||||
}
|
||||
|
||||
static int rcar_can_close(struct net_device *ndev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
|
||||
netif_stop_queue(ndev);
|
||||
rcar_can_stop(ndev);
|
||||
free_irq(ndev->irq, ndev);
|
||||
napi_disable(&priv->napi);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
close_candev(ndev);
|
||||
can_led_event(ndev, CAN_LED_EVENT_STOP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *ndev)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
struct can_frame *cf = (struct can_frame *)skb->data;
|
||||
u32 data, i;
|
||||
|
||||
if (can_dropped_invalid_skb(ndev, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */
|
||||
data = (cf->can_id & CAN_EFF_MASK) | RCAR_CAN_IDE;
|
||||
else /* Standard frame format */
|
||||
data = (cf->can_id & CAN_SFF_MASK) << RCAR_CAN_SID_SHIFT;
|
||||
|
||||
if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */
|
||||
data |= RCAR_CAN_RTR;
|
||||
} else {
|
||||
for (i = 0; i < cf->can_dlc; i++)
|
||||
writeb(cf->data[i],
|
||||
&priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].data[i]);
|
||||
}
|
||||
|
||||
writel(data, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].id);
|
||||
|
||||
writeb(cf->can_dlc, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc);
|
||||
|
||||
priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->can_dlc;
|
||||
can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH);
|
||||
priv->tx_head++;
|
||||
/* Start Tx: write 0xff to the TFPCR register to increment
|
||||
* the CPU-side pointer for the transmit FIFO to the next
|
||||
* mailbox location
|
||||
*/
|
||||
writeb(0xff, &priv->regs->tfpcr);
|
||||
/* Stop the queue if we've filled all FIFO entries */
|
||||
if (priv->tx_head - priv->tx_tail >= RCAR_CAN_FIFO_DEPTH)
|
||||
netif_stop_queue(ndev);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static const struct net_device_ops rcar_can_netdev_ops = {
|
||||
.ndo_open = rcar_can_open,
|
||||
.ndo_stop = rcar_can_close,
|
||||
.ndo_start_xmit = rcar_can_start_xmit,
|
||||
};
|
||||
|
||||
static void rcar_can_rx_pkt(struct rcar_can_priv *priv)
|
||||
{
|
||||
struct net_device_stats *stats = &priv->ndev->stats;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
u32 data;
|
||||
u8 dlc;
|
||||
|
||||
skb = alloc_can_skb(priv->ndev, &cf);
|
||||
if (!skb) {
|
||||
stats->rx_dropped++;
|
||||
return;
|
||||
}
|
||||
|
||||
data = readl(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].id);
|
||||
if (data & RCAR_CAN_IDE)
|
||||
cf->can_id = (data & CAN_EFF_MASK) | CAN_EFF_FLAG;
|
||||
else
|
||||
cf->can_id = (data >> RCAR_CAN_SID_SHIFT) & CAN_SFF_MASK;
|
||||
|
||||
dlc = readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].dlc);
|
||||
cf->can_dlc = get_can_dlc(dlc);
|
||||
if (data & RCAR_CAN_RTR) {
|
||||
cf->can_id |= CAN_RTR_FLAG;
|
||||
} else {
|
||||
for (dlc = 0; dlc < cf->can_dlc; dlc++)
|
||||
cf->data[dlc] =
|
||||
readb(&priv->regs->mb[RCAR_CAN_RX_FIFO_MBX].data[dlc]);
|
||||
}
|
||||
|
||||
can_led_event(priv->ndev, CAN_LED_EVENT_RX);
|
||||
|
||||
stats->rx_bytes += cf->can_dlc;
|
||||
stats->rx_packets++;
|
||||
netif_receive_skb(skb);
|
||||
}
|
||||
|
||||
static int rcar_can_rx_poll(struct napi_struct *napi, int quota)
|
||||
{
|
||||
struct rcar_can_priv *priv = container_of(napi,
|
||||
struct rcar_can_priv, napi);
|
||||
int num_pkts;
|
||||
|
||||
for (num_pkts = 0; num_pkts < quota; num_pkts++) {
|
||||
u8 rfcr, isr;
|
||||
|
||||
isr = readb(&priv->regs->isr);
|
||||
/* Clear interrupt bit */
|
||||
if (isr & RCAR_CAN_ISR_RXFF)
|
||||
writeb(isr & ~RCAR_CAN_ISR_RXFF, &priv->regs->isr);
|
||||
rfcr = readb(&priv->regs->rfcr);
|
||||
if (rfcr & RCAR_CAN_RFCR_RFEST)
|
||||
break;
|
||||
rcar_can_rx_pkt(priv);
|
||||
/* Write 0xff to the RFPCR register to increment
|
||||
* the CPU-side pointer for the receive FIFO
|
||||
* to the next mailbox location
|
||||
*/
|
||||
writeb(0xff, &priv->regs->rfpcr);
|
||||
}
|
||||
/* All packets processed */
|
||||
if (num_pkts < quota) {
|
||||
napi_complete(napi);
|
||||
priv->ier |= RCAR_CAN_IER_RXFIE;
|
||||
writeb(priv->ier, &priv->regs->ier);
|
||||
}
|
||||
return num_pkts;
|
||||
}
|
||||
|
||||
static int rcar_can_do_set_mode(struct net_device *ndev, enum can_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case CAN_MODE_START:
|
||||
rcar_can_start(ndev);
|
||||
netif_wake_queue(ndev);
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_can_get_berr_counter(const struct net_device *dev,
|
||||
struct can_berr_counter *bec)
|
||||
{
|
||||
struct rcar_can_priv *priv = netdev_priv(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(priv->clk);
|
||||
if (err)
|
||||
return err;
|
||||
bec->txerr = readb(&priv->regs->tecr);
|
||||
bec->rxerr = readb(&priv->regs->recr);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_can_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_can_platform_data *pdata;
|
||||
struct rcar_can_priv *priv;
|
||||
struct net_device *ndev;
|
||||
struct resource *mem;
|
||||
void __iomem *addr;
|
||||
int err = -ENODEV;
|
||||
int irq;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "No platform data provided!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "No IRQ resource\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
addr = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(addr)) {
|
||||
err = PTR_ERR(addr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ndev = alloc_candev(sizeof(struct rcar_can_priv), RCAR_CAN_FIFO_DEPTH);
|
||||
if (!ndev) {
|
||||
dev_err(&pdev->dev, "alloc_candev() failed\n");
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv = netdev_priv(ndev);
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
err = PTR_ERR(priv->clk);
|
||||
dev_err(&pdev->dev, "cannot get clock: %d\n", err);
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
ndev->netdev_ops = &rcar_can_netdev_ops;
|
||||
ndev->irq = irq;
|
||||
ndev->flags |= IFF_ECHO;
|
||||
priv->ndev = ndev;
|
||||
priv->regs = addr;
|
||||
priv->clock_select = pdata->clock_select;
|
||||
priv->can.clock.freq = clk_get_rate(priv->clk);
|
||||
priv->can.bittiming_const = &rcar_can_bittiming_const;
|
||||
priv->can.do_set_mode = rcar_can_do_set_mode;
|
||||
priv->can.do_get_berr_counter = rcar_can_get_berr_counter;
|
||||
priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING;
|
||||
platform_set_drvdata(pdev, ndev);
|
||||
SET_NETDEV_DEV(ndev, &pdev->dev);
|
||||
|
||||
netif_napi_add(ndev, &priv->napi, rcar_can_rx_poll,
|
||||
RCAR_CAN_NAPI_WEIGHT);
|
||||
err = register_candev(ndev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "register_candev() failed, error %d\n",
|
||||
err);
|
||||
goto fail_candev;
|
||||
}
|
||||
|
||||
devm_can_led_init(ndev);
|
||||
|
||||
dev_info(&pdev->dev, "device registered (reg_base=%p, irq=%u)\n",
|
||||
priv->regs, ndev->irq);
|
||||
|
||||
return 0;
|
||||
fail_candev:
|
||||
netif_napi_del(&priv->napi);
|
||||
fail_clk:
|
||||
free_candev(ndev);
|
||||
fail:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rcar_can_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *ndev = platform_get_drvdata(pdev);
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
|
||||
unregister_candev(ndev);
|
||||
netif_napi_del(&priv->napi);
|
||||
free_candev(ndev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rcar_can_suspend(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
u16 ctlr;
|
||||
|
||||
if (netif_running(ndev)) {
|
||||
netif_stop_queue(ndev);
|
||||
netif_device_detach(ndev);
|
||||
}
|
||||
ctlr = readw(&priv->regs->ctlr);
|
||||
ctlr |= RCAR_CAN_CTLR_CANM_HALT;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
ctlr |= RCAR_CAN_CTLR_SLPM;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
priv->can.state = CAN_STATE_SLEEPING;
|
||||
|
||||
clk_disable(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused rcar_can_resume(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct rcar_can_priv *priv = netdev_priv(ndev);
|
||||
u16 ctlr;
|
||||
int err;
|
||||
|
||||
err = clk_enable(priv->clk);
|
||||
if (err) {
|
||||
netdev_err(ndev, "clk_enable() failed, error %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ctlr = readw(&priv->regs->ctlr);
|
||||
ctlr &= ~RCAR_CAN_CTLR_SLPM;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
ctlr &= ~RCAR_CAN_CTLR_CANM;
|
||||
writew(ctlr, &priv->regs->ctlr);
|
||||
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
if (netif_running(ndev)) {
|
||||
netif_device_attach(ndev);
|
||||
netif_start_queue(ndev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rcar_can_pm_ops, rcar_can_suspend, rcar_can_resume);
|
||||
|
||||
static struct platform_driver rcar_can_driver = {
|
||||
.driver = {
|
||||
.name = RCAR_CAN_DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &rcar_can_pm_ops,
|
||||
},
|
||||
.probe = rcar_can_probe,
|
||||
.remove = rcar_can_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(rcar_can_driver);
|
||||
|
||||
MODULE_AUTHOR("Cogent Embedded, Inc.");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("CAN driver for Renesas R-Car SoC");
|
||||
MODULE_ALIAS("platform:" RCAR_CAN_DRV_NAME);
|
||||
|
|
@ -556,15 +556,6 @@ failed:
|
|||
/*
|
||||
* netdev sysfs
|
||||
*/
|
||||
static ssize_t show_channel(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct net_device *ndev = to_net_dev(dev);
|
||||
struct softing_priv *priv = netdev2softing(ndev);
|
||||
|
||||
return sprintf(buf, "%i\n", priv->index);
|
||||
}
|
||||
|
||||
static ssize_t show_chip(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
|
|
@ -609,12 +600,10 @@ static ssize_t store_output(struct device *dev, struct device_attribute *attr,
|
|||
return count;
|
||||
}
|
||||
|
||||
static const DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
|
||||
static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL);
|
||||
static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output);
|
||||
|
||||
static const struct attribute *const netdev_sysfs_attrs[] = {
|
||||
&dev_attr_channel.attr,
|
||||
&dev_attr_chip.attr,
|
||||
&dev_attr_output.attr,
|
||||
NULL,
|
||||
|
|
@ -679,17 +668,20 @@ static int softing_netdev_register(struct net_device *netdev)
|
|||
{
|
||||
int ret;
|
||||
|
||||
netdev->sysfs_groups[0] = &netdev_sysfs_group;
|
||||
ret = register_candev(netdev);
|
||||
if (ret) {
|
||||
dev_alert(&netdev->dev, "register failed\n");
|
||||
return ret;
|
||||
}
|
||||
if (sysfs_create_group(&netdev->dev.kobj, &netdev_sysfs_group) < 0)
|
||||
netdev_alert(netdev, "sysfs group failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void softing_netdev_cleanup(struct net_device *netdev)
|
||||
{
|
||||
sysfs_remove_group(&netdev->dev.kobj, &netdev_sysfs_group);
|
||||
unregister_candev(netdev);
|
||||
free_candev(netdev);
|
||||
}
|
||||
|
|
@ -721,8 +713,6 @@ DEV_ATTR_RO(firmware_version, id.fw_version);
|
|||
DEV_ATTR_RO_STR(hardware, pdat->name);
|
||||
DEV_ATTR_RO(hardware_version, id.hw_version);
|
||||
DEV_ATTR_RO(license, id.license);
|
||||
DEV_ATTR_RO(frequency, id.freq);
|
||||
DEV_ATTR_RO(txpending, tx.pending);
|
||||
|
||||
static struct attribute *softing_pdev_attrs[] = {
|
||||
&dev_attr_serial.attr,
|
||||
|
|
@ -731,8 +721,6 @@ static struct attribute *softing_pdev_attrs[] = {
|
|||
&dev_attr_hardware.attr,
|
||||
&dev_attr_hardware_version.attr,
|
||||
&dev_attr_license.attr,
|
||||
&dev_attr_frequency.attr,
|
||||
&dev_attr_txpending.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
|
|||
10
drivers/net/can/spi/Kconfig
Normal file
10
drivers/net/can/spi/Kconfig
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
menu "CAN SPI interfaces"
|
||||
depends on SPI
|
||||
|
||||
config CAN_MCP251X
|
||||
tristate "Microchip MCP251x SPI CAN controllers"
|
||||
depends on HAS_DMA
|
||||
---help---
|
||||
Driver for the Microchip MCP251x SPI CAN controllers.
|
||||
|
||||
endmenu
|
||||
8
drivers/net/can/spi/Makefile
Normal file
8
drivers/net/can/spi/Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# Makefile for the Linux Controller Area Network SPI drivers.
|
||||
#
|
||||
|
||||
|
||||
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
|
||||
|
||||
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
|
||||
|
|
@ -214,6 +214,8 @@
|
|||
|
||||
#define TX_ECHO_SKB_MAX 1
|
||||
|
||||
#define MCP251X_OST_DELAY_MS (5)
|
||||
|
||||
#define DEVICE_NAME "mcp251x"
|
||||
|
||||
static int mcp251x_enable_dma; /* Enable SPI DMA. Default: 0 (Off) */
|
||||
|
|
@ -624,50 +626,45 @@ static int mcp251x_setup(struct net_device *net, struct mcp251x_priv *priv,
|
|||
static int mcp251x_hw_reset(struct spi_device *spi)
|
||||
{
|
||||
struct mcp251x_priv *priv = spi_get_drvdata(spi);
|
||||
u8 reg;
|
||||
int ret;
|
||||
unsigned long timeout;
|
||||
|
||||
/* Wait for oscillator startup timer after power up */
|
||||
mdelay(MCP251X_OST_DELAY_MS);
|
||||
|
||||
priv->spi_tx_buf[0] = INSTRUCTION_RESET;
|
||||
ret = spi_write(spi, priv->spi_tx_buf, 1);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "reset failed: ret = %d\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
ret = mcp251x_spi_trans(spi, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Wait for oscillator startup timer after reset */
|
||||
mdelay(MCP251X_OST_DELAY_MS);
|
||||
|
||||
reg = mcp251x_read_reg(spi, CANSTAT);
|
||||
if ((reg & CANCTRL_REQOP_MASK) != CANCTRL_REQOP_CONF)
|
||||
return -ENODEV;
|
||||
|
||||
/* Wait for reset to finish */
|
||||
timeout = jiffies + HZ;
|
||||
mdelay(10);
|
||||
while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK)
|
||||
!= CANCTRL_REQOP_CONF) {
|
||||
schedule();
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(&spi->dev, "MCP251x didn't"
|
||||
" enter in conf mode after reset\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp251x_hw_probe(struct spi_device *spi)
|
||||
{
|
||||
int st1, st2;
|
||||
u8 ctrl;
|
||||
int ret;
|
||||
|
||||
mcp251x_hw_reset(spi);
|
||||
ret = mcp251x_hw_reset(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Please note that these are "magic values" based on after
|
||||
* reset defaults taken from data sheet which allows us to see
|
||||
* if we really have a chip on the bus (we avoid common all
|
||||
* zeroes or all ones situations)
|
||||
*/
|
||||
st1 = mcp251x_read_reg(spi, CANSTAT) & 0xEE;
|
||||
st2 = mcp251x_read_reg(spi, CANCTRL) & 0x17;
|
||||
ctrl = mcp251x_read_reg(spi, CANCTRL);
|
||||
|
||||
dev_dbg(&spi->dev, "CANSTAT 0x%02x CANCTRL 0x%02x\n", st1, st2);
|
||||
dev_dbg(&spi->dev, "CANCTRL 0x%02x\n", ctrl);
|
||||
|
||||
/* Check for power up default values */
|
||||
return (st1 == 0x80 && st2 == 0x07) ? 1 : 0;
|
||||
/* Check for power up default value */
|
||||
if ((ctrl & 0x17) != 0x07)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp251x_power_enable(struct regulator *reg, int enable)
|
||||
|
|
@ -776,7 +773,6 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
|
|||
|
||||
mutex_lock(&priv->mcp_lock);
|
||||
if (priv->after_suspend) {
|
||||
mdelay(10);
|
||||
mcp251x_hw_reset(spi);
|
||||
mcp251x_setup(net, priv, spi);
|
||||
if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
|
||||
|
|
@ -955,7 +951,7 @@ static int mcp251x_open(struct net_device *net)
|
|||
priv->tx_len = 0;
|
||||
|
||||
ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist,
|
||||
flags, DEVICE_NAME, priv);
|
||||
flags | IRQF_ONESHOT, DEVICE_NAME, priv);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq);
|
||||
mcp251x_power_enable(priv->transceiver, 0);
|
||||
|
|
@ -1032,8 +1028,8 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
|||
struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
struct net_device *net;
|
||||
struct mcp251x_priv *priv;
|
||||
int freq, ret = -ENODEV;
|
||||
struct clk *clk;
|
||||
int freq, ret;
|
||||
|
||||
clk = devm_clk_get(&spi->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
|
|
@ -1076,6 +1072,18 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
|||
priv->net = net;
|
||||
priv->clk = clk;
|
||||
|
||||
spi_set_drvdata(spi, priv);
|
||||
|
||||
/* Configure the SPI bus */
|
||||
spi->bits_per_word = 8;
|
||||
if (mcp251x_is_2510(spi))
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
|
||||
else
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
|
||||
ret = spi_setup(spi);
|
||||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
priv->power = devm_regulator_get(&spi->dev, "vdd");
|
||||
priv->transceiver = devm_regulator_get(&spi->dev, "xceiver");
|
||||
if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
|
||||
|
|
@ -1088,8 +1096,6 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto out_clk;
|
||||
|
||||
spi_set_drvdata(spi, priv);
|
||||
|
||||
priv->spi = spi;
|
||||
mutex_init(&priv->mcp_lock);
|
||||
|
||||
|
|
@ -1134,20 +1140,11 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
|||
|
||||
SET_NETDEV_DEV(net, &spi->dev);
|
||||
|
||||
/* Configure the SPI bus */
|
||||
spi->mode = spi->mode ? : SPI_MODE_0;
|
||||
if (mcp251x_is_2510(spi))
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000;
|
||||
else
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
|
||||
spi->bits_per_word = 8;
|
||||
spi_setup(spi);
|
||||
|
||||
/* Here is OK to not lock the MCP, no one knows about it yet */
|
||||
if (!mcp251x_hw_probe(spi)) {
|
||||
ret = -ENODEV;
|
||||
ret = mcp251x_hw_probe(spi);
|
||||
if (ret)
|
||||
goto error_probe;
|
||||
}
|
||||
|
||||
mcp251x_hw_sleep(spi);
|
||||
|
||||
ret = register_candev(net);
|
||||
|
|
@ -1156,7 +1153,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
|
|||
|
||||
devm_can_led_init(net);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
error_probe:
|
||||
if (mcp251x_enable_dma)
|
||||
|
|
@ -13,13 +13,21 @@ config CAN_ESD_USB2
|
|||
This driver supports the CAN-USB/2 interface
|
||||
from esd electronic system design gmbh (http://www.esd.eu).
|
||||
|
||||
config CAN_GS_USB
|
||||
tristate "Geschwister Schneider UG interfaces"
|
||||
---help---
|
||||
This driver supports the Geschwister Schneider USB/CAN devices.
|
||||
If unsure choose N,
|
||||
choose Y for built in support,
|
||||
M to compile as module (module will be named: gs_usb).
|
||||
|
||||
config CAN_KVASER_USB
|
||||
tristate "Kvaser CAN/USB interface"
|
||||
---help---
|
||||
This driver adds support for Kvaser CAN/USB devices like Kvaser
|
||||
Leaf Light.
|
||||
|
||||
The driver gives support for the following devices:
|
||||
The driver provides support for the following devices:
|
||||
- Kvaser Leaf Light
|
||||
- Kvaser Leaf Professional HS
|
||||
- Kvaser Leaf SemiPro HS
|
||||
|
|
@ -36,6 +44,8 @@ config CAN_KVASER_USB
|
|||
- Kvaser Leaf Light "China"
|
||||
- Kvaser BlackBird SemiPro
|
||||
- Kvaser USBcan R
|
||||
- Kvaser Leaf Light v2
|
||||
- Kvaser Mini PCI Express HS
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
|
||||
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
|
||||
obj-$(CONFIG_CAN_GS_USB) += gs_usb.o
|
||||
obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
|
||||
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
|
||||
obj-$(CONFIG_CAN_8DEV_USB) += usb_8dev.o
|
||||
|
|
|
|||
971
drivers/net/can/usb/gs_usb.c
Normal file
971
drivers/net/can/usb/gs_usb.c
Normal file
|
|
@ -0,0 +1,971 @@
|
|||
/* CAN driver for Geschwister Schneider USB/CAN devices.
|
||||
*
|
||||
* Copyright (C) 2013 Geschwister Schneider Technologie-,
|
||||
* Entwicklungs- und Vertriebs UG (Haftungsbeschränkt).
|
||||
*
|
||||
* Many thanks to all socketcan devs!
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/dev.h>
|
||||
#include <linux/can/error.h>
|
||||
|
||||
/* Device specific constants */
|
||||
#define USB_GSUSB_1_VENDOR_ID 0x1d50
|
||||
#define USB_GSUSB_1_PRODUCT_ID 0x606f
|
||||
|
||||
#define GSUSB_ENDPOINT_IN 1
|
||||
#define GSUSB_ENDPOINT_OUT 2
|
||||
|
||||
/* Device specific constants */
|
||||
enum gs_usb_breq {
|
||||
GS_USB_BREQ_HOST_FORMAT = 0,
|
||||
GS_USB_BREQ_BITTIMING,
|
||||
GS_USB_BREQ_MODE,
|
||||
GS_USB_BREQ_BERR,
|
||||
GS_USB_BREQ_BT_CONST,
|
||||
GS_USB_BREQ_DEVICE_CONFIG
|
||||
};
|
||||
|
||||
enum gs_can_mode {
|
||||
/* reset a channel. turns it off */
|
||||
GS_CAN_MODE_RESET = 0,
|
||||
/* starts a channel */
|
||||
GS_CAN_MODE_START
|
||||
};
|
||||
|
||||
enum gs_can_state {
|
||||
GS_CAN_STATE_ERROR_ACTIVE = 0,
|
||||
GS_CAN_STATE_ERROR_WARNING,
|
||||
GS_CAN_STATE_ERROR_PASSIVE,
|
||||
GS_CAN_STATE_BUS_OFF,
|
||||
GS_CAN_STATE_STOPPED,
|
||||
GS_CAN_STATE_SLEEPING
|
||||
};
|
||||
|
||||
/* data types passed between host and device */
|
||||
struct gs_host_config {
|
||||
u32 byte_order;
|
||||
} __packed;
|
||||
/* All data exchanged between host and device is exchanged in host byte order,
|
||||
* thanks to the struct gs_host_config byte_order member, which is sent first
|
||||
* to indicate the desired byte order.
|
||||
*/
|
||||
|
||||
struct gs_device_config {
|
||||
u8 reserved1;
|
||||
u8 reserved2;
|
||||
u8 reserved3;
|
||||
u8 icount;
|
||||
u32 sw_version;
|
||||
u32 hw_version;
|
||||
} __packed;
|
||||
|
||||
#define GS_CAN_MODE_NORMAL 0
|
||||
#define GS_CAN_MODE_LISTEN_ONLY (1<<0)
|
||||
#define GS_CAN_MODE_LOOP_BACK (1<<1)
|
||||
#define GS_CAN_MODE_TRIPLE_SAMPLE (1<<2)
|
||||
#define GS_CAN_MODE_ONE_SHOT (1<<3)
|
||||
|
||||
struct gs_device_mode {
|
||||
u32 mode;
|
||||
u32 flags;
|
||||
} __packed;
|
||||
|
||||
struct gs_device_state {
|
||||
u32 state;
|
||||
u32 rxerr;
|
||||
u32 txerr;
|
||||
} __packed;
|
||||
|
||||
struct gs_device_bittiming {
|
||||
u32 prop_seg;
|
||||
u32 phase_seg1;
|
||||
u32 phase_seg2;
|
||||
u32 sjw;
|
||||
u32 brp;
|
||||
} __packed;
|
||||
|
||||
#define GS_CAN_FEATURE_LISTEN_ONLY (1<<0)
|
||||
#define GS_CAN_FEATURE_LOOP_BACK (1<<1)
|
||||
#define GS_CAN_FEATURE_TRIPLE_SAMPLE (1<<2)
|
||||
#define GS_CAN_FEATURE_ONE_SHOT (1<<3)
|
||||
|
||||
struct gs_device_bt_const {
|
||||
u32 feature;
|
||||
u32 fclk_can;
|
||||
u32 tseg1_min;
|
||||
u32 tseg1_max;
|
||||
u32 tseg2_min;
|
||||
u32 tseg2_max;
|
||||
u32 sjw_max;
|
||||
u32 brp_min;
|
||||
u32 brp_max;
|
||||
u32 brp_inc;
|
||||
} __packed;
|
||||
|
||||
#define GS_CAN_FLAG_OVERFLOW 1
|
||||
|
||||
struct gs_host_frame {
|
||||
u32 echo_id;
|
||||
u32 can_id;
|
||||
|
||||
u8 can_dlc;
|
||||
u8 channel;
|
||||
u8 flags;
|
||||
u8 reserved;
|
||||
|
||||
u8 data[8];
|
||||
} __packed;
|
||||
/* The GS USB devices make use of the same flags and masks as in
|
||||
* linux/can.h and linux/can/error.h, and no additional mapping is necessary.
|
||||
*/
|
||||
|
||||
/* Only send a max of GS_MAX_TX_URBS frames per channel at a time. */
|
||||
#define GS_MAX_TX_URBS 10
|
||||
/* Only launch a max of GS_MAX_RX_URBS usb requests at a time. */
|
||||
#define GS_MAX_RX_URBS 30
|
||||
/* Maximum number of interfaces the driver supports per device.
|
||||
* Current hardware only supports 2 interfaces. The future may vary.
|
||||
*/
|
||||
#define GS_MAX_INTF 2
|
||||
|
||||
struct gs_tx_context {
|
||||
struct gs_can *dev;
|
||||
unsigned int echo_id;
|
||||
};
|
||||
|
||||
struct gs_can {
|
||||
struct can_priv can; /* must be the first member */
|
||||
|
||||
struct gs_usb *parent;
|
||||
|
||||
struct net_device *netdev;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *iface;
|
||||
|
||||
struct can_bittiming_const bt_const;
|
||||
unsigned int channel; /* channel number */
|
||||
|
||||
/* This lock prevents a race condition between xmit and recieve. */
|
||||
spinlock_t tx_ctx_lock;
|
||||
struct gs_tx_context tx_context[GS_MAX_TX_URBS];
|
||||
|
||||
struct usb_anchor tx_submitted;
|
||||
atomic_t active_tx_urbs;
|
||||
};
|
||||
|
||||
/* usb interface struct */
|
||||
struct gs_usb {
|
||||
struct gs_can *canch[GS_MAX_INTF];
|
||||
struct usb_anchor rx_submitted;
|
||||
atomic_t active_channels;
|
||||
struct usb_device *udev;
|
||||
};
|
||||
|
||||
/* 'allocate' a tx context.
|
||||
* returns a valid tx context or NULL if there is no space.
|
||||
*/
|
||||
static struct gs_tx_context *gs_alloc_tx_context(struct gs_can *dev)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->tx_ctx_lock, flags);
|
||||
|
||||
for (; i < GS_MAX_TX_URBS; i++) {
|
||||
if (dev->tx_context[i].echo_id == GS_MAX_TX_URBS) {
|
||||
dev->tx_context[i].echo_id = i;
|
||||
spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
|
||||
return &dev->tx_context[i];
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* releases a tx context
|
||||
*/
|
||||
static void gs_free_tx_context(struct gs_tx_context *txc)
|
||||
{
|
||||
txc->echo_id = GS_MAX_TX_URBS;
|
||||
}
|
||||
|
||||
/* Get a tx context by id.
|
||||
*/
|
||||
static struct gs_tx_context *gs_get_tx_context(struct gs_can *dev, unsigned int id)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (id < GS_MAX_TX_URBS) {
|
||||
spin_lock_irqsave(&dev->tx_ctx_lock, flags);
|
||||
if (dev->tx_context[id].echo_id == id) {
|
||||
spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
|
||||
return &dev->tx_context[id];
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->tx_ctx_lock, flags);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int gs_cmd_reset(struct gs_usb *gsusb, struct gs_can *gsdev)
|
||||
{
|
||||
struct gs_device_mode *dm;
|
||||
struct usb_interface *intf = gsdev->iface;
|
||||
int rc;
|
||||
|
||||
dm = kzalloc(sizeof(*dm), GFP_KERNEL);
|
||||
if (!dm)
|
||||
return -ENOMEM;
|
||||
|
||||
dm->mode = GS_CAN_MODE_RESET;
|
||||
|
||||
rc = usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
||||
GS_USB_BREQ_MODE,
|
||||
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||
gsdev->channel,
|
||||
0,
|
||||
dm,
|
||||
sizeof(*dm),
|
||||
1000);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void gs_update_state(struct gs_can *dev, struct can_frame *cf)
|
||||
{
|
||||
struct can_device_stats *can_stats = &dev->can.can_stats;
|
||||
|
||||
if (cf->can_id & CAN_ERR_RESTARTED) {
|
||||
dev->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
can_stats->restarts++;
|
||||
} else if (cf->can_id & CAN_ERR_BUSOFF) {
|
||||
dev->can.state = CAN_STATE_BUS_OFF;
|
||||
can_stats->bus_off++;
|
||||
} else if (cf->can_id & CAN_ERR_CRTL) {
|
||||
if ((cf->data[1] & CAN_ERR_CRTL_TX_WARNING) ||
|
||||
(cf->data[1] & CAN_ERR_CRTL_RX_WARNING)) {
|
||||
dev->can.state = CAN_STATE_ERROR_WARNING;
|
||||
can_stats->error_warning++;
|
||||
} else if ((cf->data[1] & CAN_ERR_CRTL_TX_PASSIVE) ||
|
||||
(cf->data[1] & CAN_ERR_CRTL_RX_PASSIVE)) {
|
||||
dev->can.state = CAN_STATE_ERROR_PASSIVE;
|
||||
can_stats->error_passive++;
|
||||
} else {
|
||||
dev->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_usb_recieve_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct gs_usb *usbcan = urb->context;
|
||||
struct gs_can *dev;
|
||||
struct net_device *netdev;
|
||||
int rc;
|
||||
struct net_device_stats *stats;
|
||||
struct gs_host_frame *hf = urb->transfer_buffer;
|
||||
struct gs_tx_context *txc;
|
||||
struct can_frame *cf;
|
||||
struct sk_buff *skb;
|
||||
|
||||
BUG_ON(!usbcan);
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
default:
|
||||
/* do not resubmit aborted urbs. eg: when device goes down */
|
||||
return;
|
||||
}
|
||||
|
||||
/* device reports out of range channel id */
|
||||
if (hf->channel >= GS_MAX_INTF)
|
||||
goto resubmit_urb;
|
||||
|
||||
dev = usbcan->canch[hf->channel];
|
||||
|
||||
netdev = dev->netdev;
|
||||
stats = &netdev->stats;
|
||||
|
||||
if (!netif_device_present(netdev))
|
||||
return;
|
||||
|
||||
if (hf->echo_id == -1) { /* normal rx */
|
||||
skb = alloc_can_skb(dev->netdev, &cf);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
cf->can_id = hf->can_id;
|
||||
|
||||
cf->can_dlc = get_can_dlc(hf->can_dlc);
|
||||
memcpy(cf->data, hf->data, 8);
|
||||
|
||||
/* ERROR frames tell us information about the controller */
|
||||
if (hf->can_id & CAN_ERR_FLAG)
|
||||
gs_update_state(dev, cf);
|
||||
|
||||
netdev->stats.rx_packets++;
|
||||
netdev->stats.rx_bytes += hf->can_dlc;
|
||||
|
||||
netif_rx(skb);
|
||||
} else { /* echo_id == hf->echo_id */
|
||||
if (hf->echo_id >= GS_MAX_TX_URBS) {
|
||||
netdev_err(netdev,
|
||||
"Unexpected out of range echo id %d\n",
|
||||
hf->echo_id);
|
||||
goto resubmit_urb;
|
||||
}
|
||||
|
||||
netdev->stats.tx_packets++;
|
||||
netdev->stats.tx_bytes += hf->can_dlc;
|
||||
|
||||
txc = gs_get_tx_context(dev, hf->echo_id);
|
||||
|
||||
/* bad devices send bad echo_ids. */
|
||||
if (!txc) {
|
||||
netdev_err(netdev,
|
||||
"Unexpected unused echo id %d\n",
|
||||
hf->echo_id);
|
||||
goto resubmit_urb;
|
||||
}
|
||||
|
||||
can_get_echo_skb(netdev, hf->echo_id);
|
||||
|
||||
gs_free_tx_context(txc);
|
||||
|
||||
netif_wake_queue(netdev);
|
||||
}
|
||||
|
||||
if (hf->flags & GS_CAN_FLAG_OVERFLOW) {
|
||||
skb = alloc_can_err_skb(netdev, &cf);
|
||||
if (!skb)
|
||||
goto resubmit_urb;
|
||||
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->can_dlc = CAN_ERR_DLC;
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
stats->rx_over_errors++;
|
||||
stats->rx_errors++;
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
resubmit_urb:
|
||||
usb_fill_bulk_urb(urb,
|
||||
usbcan->udev,
|
||||
usb_rcvbulkpipe(usbcan->udev, GSUSB_ENDPOINT_IN),
|
||||
hf,
|
||||
sizeof(struct gs_host_frame),
|
||||
gs_usb_recieve_bulk_callback,
|
||||
usbcan
|
||||
);
|
||||
|
||||
rc = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
|
||||
/* USB failure take down all interfaces */
|
||||
if (rc == -ENODEV) {
|
||||
for (rc = 0; rc < GS_MAX_INTF; rc++) {
|
||||
if (usbcan->canch[rc])
|
||||
netif_device_detach(usbcan->canch[rc]->netdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gs_usb_set_bittiming(struct net_device *netdev)
|
||||
{
|
||||
struct gs_can *dev = netdev_priv(netdev);
|
||||
struct can_bittiming *bt = &dev->can.bittiming;
|
||||
struct usb_interface *intf = dev->iface;
|
||||
int rc;
|
||||
struct gs_device_bittiming *dbt;
|
||||
|
||||
dbt = kmalloc(sizeof(*dbt), GFP_KERNEL);
|
||||
if (!dbt)
|
||||
return -ENOMEM;
|
||||
|
||||
dbt->prop_seg = bt->prop_seg;
|
||||
dbt->phase_seg1 = bt->phase_seg1;
|
||||
dbt->phase_seg2 = bt->phase_seg2;
|
||||
dbt->sjw = bt->sjw;
|
||||
dbt->brp = bt->brp;
|
||||
|
||||
/* request bit timings */
|
||||
rc = usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
||||
GS_USB_BREQ_BITTIMING,
|
||||
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||
dev->channel,
|
||||
0,
|
||||
dbt,
|
||||
sizeof(*dbt),
|
||||
1000);
|
||||
|
||||
kfree(dbt);
|
||||
|
||||
if (rc < 0)
|
||||
dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)",
|
||||
rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void gs_usb_xmit_callback(struct urb *urb)
|
||||
{
|
||||
struct gs_tx_context *txc = urb->context;
|
||||
struct gs_can *dev = txc->dev;
|
||||
struct net_device *netdev = dev->netdev;
|
||||
|
||||
if (urb->status)
|
||||
netdev_info(netdev, "usb xmit fail %d\n", txc->echo_id);
|
||||
|
||||
usb_free_coherent(urb->dev,
|
||||
urb->transfer_buffer_length,
|
||||
urb->transfer_buffer,
|
||||
urb->transfer_dma);
|
||||
|
||||
atomic_dec(&dev->active_tx_urbs);
|
||||
|
||||
if (!netif_device_present(netdev))
|
||||
return;
|
||||
|
||||
if (netif_queue_stopped(netdev))
|
||||
netif_wake_queue(netdev);
|
||||
}
|
||||
|
||||
static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
struct gs_can *dev = netdev_priv(netdev);
|
||||
struct net_device_stats *stats = &dev->netdev->stats;
|
||||
struct urb *urb;
|
||||
struct gs_host_frame *hf;
|
||||
struct can_frame *cf;
|
||||
int rc;
|
||||
unsigned int idx;
|
||||
struct gs_tx_context *txc;
|
||||
|
||||
if (can_dropped_invalid_skb(netdev, skb))
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
/* find an empty context to keep track of transmission */
|
||||
txc = gs_alloc_tx_context(dev);
|
||||
if (!txc)
|
||||
return NETDEV_TX_BUSY;
|
||||
|
||||
/* create a URB, and a buffer for it */
|
||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
if (!urb) {
|
||||
netdev_err(netdev, "No memory left for URB\n");
|
||||
goto nomem_urb;
|
||||
}
|
||||
|
||||
hf = usb_alloc_coherent(dev->udev, sizeof(*hf), GFP_ATOMIC,
|
||||
&urb->transfer_dma);
|
||||
if (!hf) {
|
||||
netdev_err(netdev, "No memory left for USB buffer\n");
|
||||
goto nomem_hf;
|
||||
}
|
||||
|
||||
idx = txc->echo_id;
|
||||
|
||||
if (idx >= GS_MAX_TX_URBS) {
|
||||
netdev_err(netdev, "Invalid tx context %d\n", idx);
|
||||
goto badidx;
|
||||
}
|
||||
|
||||
hf->echo_id = idx;
|
||||
hf->channel = dev->channel;
|
||||
|
||||
cf = (struct can_frame *)skb->data;
|
||||
|
||||
hf->can_id = cf->can_id;
|
||||
hf->can_dlc = cf->can_dlc;
|
||||
memcpy(hf->data, cf->data, cf->can_dlc);
|
||||
|
||||
usb_fill_bulk_urb(urb, dev->udev,
|
||||
usb_sndbulkpipe(dev->udev, GSUSB_ENDPOINT_OUT),
|
||||
hf,
|
||||
sizeof(*hf),
|
||||
gs_usb_xmit_callback,
|
||||
txc);
|
||||
|
||||
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
usb_anchor_urb(urb, &dev->tx_submitted);
|
||||
|
||||
can_put_echo_skb(skb, netdev, idx);
|
||||
|
||||
atomic_inc(&dev->active_tx_urbs);
|
||||
|
||||
rc = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (unlikely(rc)) { /* usb send failed */
|
||||
atomic_dec(&dev->active_tx_urbs);
|
||||
|
||||
can_free_echo_skb(netdev, idx);
|
||||
gs_free_tx_context(txc);
|
||||
|
||||
usb_unanchor_urb(urb);
|
||||
usb_free_coherent(dev->udev,
|
||||
sizeof(*hf),
|
||||
hf,
|
||||
urb->transfer_dma);
|
||||
|
||||
|
||||
if (rc == -ENODEV) {
|
||||
netif_device_detach(netdev);
|
||||
} else {
|
||||
netdev_err(netdev, "usb_submit failed (err=%d)\n", rc);
|
||||
stats->tx_dropped++;
|
||||
}
|
||||
} else {
|
||||
/* Slow down tx path */
|
||||
if (atomic_read(&dev->active_tx_urbs) >= GS_MAX_TX_URBS)
|
||||
netif_stop_queue(netdev);
|
||||
}
|
||||
|
||||
/* let usb core take care of this urb */
|
||||
usb_free_urb(urb);
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
badidx:
|
||||
usb_free_coherent(dev->udev,
|
||||
sizeof(*hf),
|
||||
hf,
|
||||
urb->transfer_dma);
|
||||
nomem_hf:
|
||||
usb_free_urb(urb);
|
||||
|
||||
nomem_urb:
|
||||
gs_free_tx_context(txc);
|
||||
dev_kfree_skb(skb);
|
||||
stats->tx_dropped++;
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static int gs_can_open(struct net_device *netdev)
|
||||
{
|
||||
struct gs_can *dev = netdev_priv(netdev);
|
||||
struct gs_usb *parent = dev->parent;
|
||||
int rc, i;
|
||||
struct gs_device_mode *dm;
|
||||
u32 ctrlmode;
|
||||
|
||||
rc = open_candev(netdev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (atomic_add_return(1, &parent->active_channels) == 1) {
|
||||
for (i = 0; i < GS_MAX_RX_URBS; i++) {
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
|
||||
/* alloc rx urb */
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb) {
|
||||
netdev_err(netdev,
|
||||
"No memory left for URB\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* alloc rx buffer */
|
||||
buf = usb_alloc_coherent(dev->udev,
|
||||
sizeof(struct gs_host_frame),
|
||||
GFP_KERNEL,
|
||||
&urb->transfer_dma);
|
||||
if (!buf) {
|
||||
netdev_err(netdev,
|
||||
"No memory left for USB buffer\n");
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* fill, anchor, and submit rx urb */
|
||||
usb_fill_bulk_urb(urb,
|
||||
dev->udev,
|
||||
usb_rcvbulkpipe(dev->udev,
|
||||
GSUSB_ENDPOINT_IN),
|
||||
buf,
|
||||
sizeof(struct gs_host_frame),
|
||||
gs_usb_recieve_bulk_callback,
|
||||
parent);
|
||||
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
usb_anchor_urb(urb, &parent->rx_submitted);
|
||||
|
||||
rc = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (rc) {
|
||||
if (rc == -ENODEV)
|
||||
netif_device_detach(dev->netdev);
|
||||
|
||||
netdev_err(netdev,
|
||||
"usb_submit failed (err=%d)\n",
|
||||
rc);
|
||||
|
||||
usb_unanchor_urb(urb);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Drop reference,
|
||||
* USB core will take care of freeing it
|
||||
*/
|
||||
usb_free_urb(urb);
|
||||
}
|
||||
}
|
||||
|
||||
dm = kmalloc(sizeof(*dm), GFP_KERNEL);
|
||||
if (!dm)
|
||||
return -ENOMEM;
|
||||
|
||||
/* flags */
|
||||
ctrlmode = dev->can.ctrlmode;
|
||||
dm->flags = 0;
|
||||
|
||||
if (ctrlmode & CAN_CTRLMODE_LOOPBACK)
|
||||
dm->flags |= GS_CAN_MODE_LOOP_BACK;
|
||||
else if (ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||||
dm->flags |= GS_CAN_MODE_LISTEN_ONLY;
|
||||
|
||||
/* Controller is not allowed to retry TX
|
||||
* this mode is unavailable on atmels uc3c hardware
|
||||
*/
|
||||
if (ctrlmode & CAN_CTRLMODE_ONE_SHOT)
|
||||
dm->flags |= GS_CAN_MODE_ONE_SHOT;
|
||||
|
||||
if (ctrlmode & CAN_CTRLMODE_3_SAMPLES)
|
||||
dm->flags |= GS_CAN_MODE_TRIPLE_SAMPLE;
|
||||
|
||||
/* finally start device */
|
||||
dm->mode = GS_CAN_MODE_START;
|
||||
rc = usb_control_msg(interface_to_usbdev(dev->iface),
|
||||
usb_sndctrlpipe(interface_to_usbdev(dev->iface), 0),
|
||||
GS_USB_BREQ_MODE,
|
||||
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||
dev->channel,
|
||||
0,
|
||||
dm,
|
||||
sizeof(*dm),
|
||||
1000);
|
||||
|
||||
if (rc < 0) {
|
||||
netdev_err(netdev, "Couldn't start device (err=%d)\n", rc);
|
||||
kfree(dm);
|
||||
return rc;
|
||||
}
|
||||
|
||||
kfree(dm);
|
||||
|
||||
dev->can.state = CAN_STATE_ERROR_ACTIVE;
|
||||
|
||||
if (!(dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY))
|
||||
netif_start_queue(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gs_can_close(struct net_device *netdev)
|
||||
{
|
||||
int rc;
|
||||
struct gs_can *dev = netdev_priv(netdev);
|
||||
struct gs_usb *parent = dev->parent;
|
||||
|
||||
netif_stop_queue(netdev);
|
||||
|
||||
/* Stop polling */
|
||||
if (atomic_dec_and_test(&parent->active_channels))
|
||||
usb_kill_anchored_urbs(&parent->rx_submitted);
|
||||
|
||||
/* Stop sending URBs */
|
||||
usb_kill_anchored_urbs(&dev->tx_submitted);
|
||||
atomic_set(&dev->active_tx_urbs, 0);
|
||||
|
||||
/* reset the device */
|
||||
rc = gs_cmd_reset(parent, dev);
|
||||
if (rc < 0)
|
||||
netdev_warn(netdev, "Couldn't shutdown device (err=%d)", rc);
|
||||
|
||||
/* reset tx contexts */
|
||||
for (rc = 0; rc < GS_MAX_TX_URBS; rc++) {
|
||||
dev->tx_context[rc].dev = dev;
|
||||
dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
|
||||
}
|
||||
|
||||
/* close the netdev */
|
||||
close_candev(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct net_device_ops gs_usb_netdev_ops = {
|
||||
.ndo_open = gs_can_open,
|
||||
.ndo_stop = gs_can_close,
|
||||
.ndo_start_xmit = gs_can_start_xmit,
|
||||
};
|
||||
|
||||
static struct gs_can *gs_make_candev(unsigned int channel, struct usb_interface *intf)
|
||||
{
|
||||
struct gs_can *dev;
|
||||
struct net_device *netdev;
|
||||
int rc;
|
||||
struct gs_device_bt_const *bt_const;
|
||||
|
||||
bt_const = kmalloc(sizeof(*bt_const), GFP_KERNEL);
|
||||
if (!bt_const)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* fetch bit timing constants */
|
||||
rc = usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
|
||||
GS_USB_BREQ_BT_CONST,
|
||||
USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||
channel,
|
||||
0,
|
||||
bt_const,
|
||||
sizeof(*bt_const),
|
||||
1000);
|
||||
|
||||
if (rc < 0) {
|
||||
dev_err(&intf->dev,
|
||||
"Couldn't get bit timing const for channel (err=%d)\n",
|
||||
rc);
|
||||
kfree(bt_const);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
/* create netdev */
|
||||
netdev = alloc_candev(sizeof(struct gs_can), GS_MAX_TX_URBS);
|
||||
if (!netdev) {
|
||||
dev_err(&intf->dev, "Couldn't allocate candev\n");
|
||||
kfree(bt_const);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
dev = netdev_priv(netdev);
|
||||
|
||||
netdev->netdev_ops = &gs_usb_netdev_ops;
|
||||
|
||||
netdev->flags |= IFF_ECHO; /* we support full roundtrip echo */
|
||||
|
||||
/* dev settup */
|
||||
strcpy(dev->bt_const.name, "gs_usb");
|
||||
dev->bt_const.tseg1_min = bt_const->tseg1_min;
|
||||
dev->bt_const.tseg1_max = bt_const->tseg1_max;
|
||||
dev->bt_const.tseg2_min = bt_const->tseg2_min;
|
||||
dev->bt_const.tseg2_max = bt_const->tseg2_max;
|
||||
dev->bt_const.sjw_max = bt_const->sjw_max;
|
||||
dev->bt_const.brp_min = bt_const->brp_min;
|
||||
dev->bt_const.brp_max = bt_const->brp_max;
|
||||
dev->bt_const.brp_inc = bt_const->brp_inc;
|
||||
|
||||
dev->udev = interface_to_usbdev(intf);
|
||||
dev->iface = intf;
|
||||
dev->netdev = netdev;
|
||||
dev->channel = channel;
|
||||
|
||||
init_usb_anchor(&dev->tx_submitted);
|
||||
atomic_set(&dev->active_tx_urbs, 0);
|
||||
spin_lock_init(&dev->tx_ctx_lock);
|
||||
for (rc = 0; rc < GS_MAX_TX_URBS; rc++) {
|
||||
dev->tx_context[rc].dev = dev;
|
||||
dev->tx_context[rc].echo_id = GS_MAX_TX_URBS;
|
||||
}
|
||||
|
||||
/* can settup */
|
||||
dev->can.state = CAN_STATE_STOPPED;
|
||||
dev->can.clock.freq = bt_const->fclk_can;
|
||||
dev->can.bittiming_const = &dev->bt_const;
|
||||
dev->can.do_set_bittiming = gs_usb_set_bittiming;
|
||||
|
||||
dev->can.ctrlmode_supported = 0;
|
||||
|
||||
if (bt_const->feature & GS_CAN_FEATURE_LISTEN_ONLY)
|
||||
dev->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
|
||||
|
||||
if (bt_const->feature & GS_CAN_FEATURE_LOOP_BACK)
|
||||
dev->can.ctrlmode_supported |= CAN_CTRLMODE_LOOPBACK;
|
||||
|
||||
if (bt_const->feature & GS_CAN_FEATURE_TRIPLE_SAMPLE)
|
||||
dev->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
|
||||
|
||||
if (bt_const->feature & GS_CAN_FEATURE_ONE_SHOT)
|
||||
dev->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT;
|
||||
|
||||
kfree(bt_const);
|
||||
|
||||
SET_NETDEV_DEV(netdev, &intf->dev);
|
||||
|
||||
rc = register_candev(dev->netdev);
|
||||
if (rc) {
|
||||
free_candev(dev->netdev);
|
||||
dev_err(&intf->dev, "Couldn't register candev (err=%d)\n", rc);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void gs_destroy_candev(struct gs_can *dev)
|
||||
{
|
||||
unregister_candev(dev->netdev);
|
||||
free_candev(dev->netdev);
|
||||
usb_kill_anchored_urbs(&dev->tx_submitted);
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
static int gs_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct gs_usb *dev;
|
||||
int rc = -ENOMEM;
|
||||
unsigned int icount, i;
|
||||
struct gs_host_config *hconf;
|
||||
struct gs_device_config *dconf;
|
||||
|
||||
hconf = kmalloc(sizeof(*hconf), GFP_KERNEL);
|
||||
if (!hconf)
|
||||
return -ENOMEM;
|
||||
|
||||
hconf->byte_order = 0x0000beef;
|
||||
|
||||
/* send host config */
|
||||
rc = usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_sndctrlpipe(interface_to_usbdev(intf), 0),
|
||||
GS_USB_BREQ_HOST_FORMAT,
|
||||
USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||
1,
|
||||
intf->altsetting[0].desc.bInterfaceNumber,
|
||||
hconf,
|
||||
sizeof(*hconf),
|
||||
1000);
|
||||
|
||||
kfree(hconf);
|
||||
|
||||
if (rc < 0) {
|
||||
dev_err(&intf->dev, "Couldn't send data format (err=%d)\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dconf = kmalloc(sizeof(*dconf), GFP_KERNEL);
|
||||
if (!dconf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* read device config */
|
||||
rc = usb_control_msg(interface_to_usbdev(intf),
|
||||
usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
|
||||
GS_USB_BREQ_DEVICE_CONFIG,
|
||||
USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_INTERFACE,
|
||||
1,
|
||||
intf->altsetting[0].desc.bInterfaceNumber,
|
||||
dconf,
|
||||
sizeof(*dconf),
|
||||
1000);
|
||||
if (rc < 0) {
|
||||
dev_err(&intf->dev, "Couldn't get device config: (err=%d)\n",
|
||||
rc);
|
||||
|
||||
kfree(dconf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
icount = dconf->icount+1;
|
||||
|
||||
kfree(dconf);
|
||||
|
||||
dev_info(&intf->dev, "Configuring for %d interfaces\n", icount);
|
||||
|
||||
if (icount > GS_MAX_INTF) {
|
||||
dev_err(&intf->dev,
|
||||
"Driver cannot handle more that %d CAN interfaces\n",
|
||||
GS_MAX_INTF);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
init_usb_anchor(&dev->rx_submitted);
|
||||
|
||||
atomic_set(&dev->active_channels, 0);
|
||||
|
||||
usb_set_intfdata(intf, dev);
|
||||
dev->udev = interface_to_usbdev(intf);
|
||||
|
||||
for (i = 0; i < icount; i++) {
|
||||
dev->canch[i] = gs_make_candev(i, intf);
|
||||
if (IS_ERR_OR_NULL(dev->canch[i])) {
|
||||
/* on failure destroy previously created candevs */
|
||||
icount = i;
|
||||
for (i = 0; i < icount; i++) {
|
||||
gs_destroy_candev(dev->canch[i]);
|
||||
dev->canch[i] = NULL;
|
||||
}
|
||||
kfree(dev);
|
||||
return rc;
|
||||
}
|
||||
dev->canch[i]->parent = dev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gs_usb_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
unsigned i;
|
||||
struct gs_usb *dev = usb_get_intfdata(intf);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
|
||||
if (!dev) {
|
||||
dev_err(&intf->dev, "Disconnect (nodata)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < GS_MAX_INTF; i++) {
|
||||
struct gs_can *can = dev->canch[i];
|
||||
|
||||
if (!can)
|
||||
continue;
|
||||
|
||||
gs_destroy_candev(can);
|
||||
}
|
||||
|
||||
usb_kill_anchored_urbs(&dev->rx_submitted);
|
||||
}
|
||||
|
||||
static const struct usb_device_id gs_usb_table[] = {
|
||||
{USB_DEVICE(USB_GSUSB_1_VENDOR_ID, USB_GSUSB_1_PRODUCT_ID)},
|
||||
{} /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, gs_usb_table);
|
||||
|
||||
static struct usb_driver gs_usb_driver = {
|
||||
.name = "gs_usb",
|
||||
.probe = gs_usb_probe,
|
||||
.disconnect = gs_usb_disconnect,
|
||||
.id_table = gs_usb_table,
|
||||
};
|
||||
|
||||
module_usb_driver(gs_usb_driver);
|
||||
|
||||
MODULE_AUTHOR("Maximilian Schneider <[email protected]>");
|
||||
MODULE_DESCRIPTION(
|
||||
"Socket CAN device driver for Geschwister Schneider Technologie-, "
|
||||
"Entwicklungs- und Vertriebs UG. USB2.0 to CAN interfaces.");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -53,6 +53,8 @@
|
|||
#define USB_OEM_MERCURY_PRODUCT_ID 34
|
||||
#define USB_OEM_LEAF_PRODUCT_ID 35
|
||||
#define USB_CAN_R_PRODUCT_ID 39
|
||||
#define USB_LEAF_LITE_V2_PRODUCT_ID 288
|
||||
#define USB_MINI_PCIE_HS_PRODUCT_ID 289
|
||||
|
||||
/* USB devices features */
|
||||
#define KVASER_HAS_SILENT_MODE BIT(0)
|
||||
|
|
@ -356,6 +358,8 @@ static const struct usb_device_id kvaser_usb_table[] = {
|
|||
.driver_info = KVASER_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
|
||||
.driver_info = KVASER_HAS_TXRX_ERRORS },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) },
|
||||
{ USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
|
||||
|
|
@ -379,38 +383,43 @@ static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
|
|||
void *buf;
|
||||
int actual_len;
|
||||
int err;
|
||||
int pos = 0;
|
||||
int pos;
|
||||
unsigned long to = jiffies + msecs_to_jiffies(USB_RECV_TIMEOUT);
|
||||
|
||||
buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
err = usb_bulk_msg(dev->udev,
|
||||
usb_rcvbulkpipe(dev->udev,
|
||||
dev->bulk_in->bEndpointAddress),
|
||||
buf, RX_BUFFER_SIZE, &actual_len,
|
||||
USB_RECV_TIMEOUT);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
while (pos <= actual_len - MSG_HEADER_LEN) {
|
||||
tmp = buf + pos;
|
||||
|
||||
if (!tmp->len)
|
||||
break;
|
||||
|
||||
if (pos + tmp->len > actual_len) {
|
||||
dev_err(dev->udev->dev.parent, "Format error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp->id == id) {
|
||||
memcpy(msg, tmp, tmp->len);
|
||||
do {
|
||||
err = usb_bulk_msg(dev->udev,
|
||||
usb_rcvbulkpipe(dev->udev,
|
||||
dev->bulk_in->bEndpointAddress),
|
||||
buf, RX_BUFFER_SIZE, &actual_len,
|
||||
USB_RECV_TIMEOUT);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
}
|
||||
|
||||
pos += tmp->len;
|
||||
}
|
||||
pos = 0;
|
||||
while (pos <= actual_len - MSG_HEADER_LEN) {
|
||||
tmp = buf + pos;
|
||||
|
||||
if (!tmp->len)
|
||||
break;
|
||||
|
||||
if (pos + tmp->len > actual_len) {
|
||||
dev_err(dev->udev->dev.parent,
|
||||
"Format error\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tmp->id == id) {
|
||||
memcpy(msg, tmp, tmp->len);
|
||||
goto end;
|
||||
}
|
||||
|
||||
pos += tmp->len;
|
||||
}
|
||||
} while (time_before(jiffies, to));
|
||||
|
||||
err = -EINVAL;
|
||||
|
||||
|
|
|
|||
1208
drivers/net/can/xilinx_can.c
Normal file
1208
drivers/net/can/xilinx_can.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -289,7 +289,7 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
|
|||
|
||||
static int mv88e6123_61_65_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ static int mv88e6131_setup_global(struct dsa_switch *ds)
|
|||
|
||||
static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int addr = REG_PORT(p);
|
||||
u16 val;
|
||||
|
||||
|
|
@ -274,7 +274,7 @@ static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
|
|||
|
||||
static int mv88e6131_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
|
|||
|
||||
int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ps->smi_mutex);
|
||||
|
|
@ -118,7 +118,7 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
|
|||
|
||||
int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ps->smi_mutex);
|
||||
|
|
@ -256,7 +256,7 @@ static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
|
|||
|
||||
static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ps->ppu_mutex);
|
||||
|
|
@ -283,7 +283,7 @@ static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
|
|||
|
||||
static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
|
||||
/* Schedule a timer to re-enable the PHY polling unit. */
|
||||
mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
|
||||
|
|
@ -292,7 +292,7 @@ static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
|
|||
|
||||
void mv88e6xxx_ppu_state_init(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
|
||||
mutex_init(&ps->ppu_mutex);
|
||||
INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
|
||||
|
|
@ -463,7 +463,7 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
|
|||
int nr_stats, struct mv88e6xxx_hw_stat *stats,
|
||||
int port, uint64_t *data)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
|
|
|
|||
|
|
@ -534,7 +534,7 @@ static int el3_common_init(struct net_device *dev)
|
|||
/* The EL3-specific entries in the device structure. */
|
||||
dev->netdev_ops = &netdev_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
SET_ETHTOOL_OPS(dev, ðtool_ops);
|
||||
dev->ethtool_ops = ðtool_ops;
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err) {
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ static int tc589_probe(struct pcmcia_device *link)
|
|||
dev->netdev_ops = &el3_netdev_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
|
||||
SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
|
||||
dev->ethtool_ops = &netdev_ethtool_ops;
|
||||
|
||||
return tc589_config(link);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2435,7 +2435,7 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
netif_napi_add(dev, &tp->napi, typhoon_poll, 16);
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
|
||||
SET_ETHTOOL_OPS(dev, &typhoon_ethtool_ops);
|
||||
dev->ethtool_ops = &typhoon_ethtool_ops;
|
||||
|
||||
/* We can handle scatter gather, up to 16 entries, and
|
||||
* we can do IP checksumming (only version 4, doh...)
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ static void ax_reset_8390(struct net_device *dev)
|
|||
|
||||
/* This check _should_not_ be necessary, omit eventually. */
|
||||
while ((ei_inb(addr + EN0_ISR) & ENISR_RESET) == 0) {
|
||||
if (jiffies - reset_start_time > 2 * HZ / 100) {
|
||||
if (time_after(jiffies, reset_start_time + 2 * HZ / 100)) {
|
||||
netdev_warn(dev, "%s: did not complete.\n", __func__);
|
||||
break;
|
||||
}
|
||||
|
|
@ -293,7 +293,7 @@ static void ax_block_output(struct net_device *dev, int count,
|
|||
dma_start = jiffies;
|
||||
|
||||
while ((ei_inb(nic_base + EN0_ISR) & ENISR_RDC) == 0) {
|
||||
if (jiffies - dma_start > 2 * HZ / 100) { /* 20ms */
|
||||
if (time_after(jiffies, dma_start + 2 * HZ / 100)) { /* 20ms */
|
||||
netdev_warn(dev, "timeout waiting for Tx RDC.\n");
|
||||
ax_reset_8390(dev);
|
||||
ax_NS8390_init(dev, 1);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ source "drivers/net/ethernet/neterion/Kconfig"
|
|||
source "drivers/net/ethernet/faraday/Kconfig"
|
||||
source "drivers/net/ethernet/freescale/Kconfig"
|
||||
source "drivers/net/ethernet/fujitsu/Kconfig"
|
||||
source "drivers/net/ethernet/hisilicon/Kconfig"
|
||||
source "drivers/net/ethernet/hp/Kconfig"
|
||||
source "drivers/net/ethernet/ibm/Kconfig"
|
||||
source "drivers/net/ethernet/intel/Kconfig"
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ obj-$(CONFIG_NET_VENDOR_EXAR) += neterion/
|
|||
obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/
|
||||
obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/
|
||||
obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/
|
||||
obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/
|
||||
obj-$(CONFIG_NET_VENDOR_HP) += hp/
|
||||
obj-$(CONFIG_NET_VENDOR_IBM) += ibm/
|
||||
obj-$(CONFIG_NET_VENDOR_INTEL) += intel/
|
||||
|
|
|
|||
|
|
@ -784,7 +784,7 @@ static int starfire_init_one(struct pci_dev *pdev,
|
|||
|
||||
dev->netdev_ops = &netdev_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
SET_ETHTOOL_OPS(dev, ðtool_ops);
|
||||
dev->ethtool_ops = ðtool_ops;
|
||||
|
||||
netif_napi_add(dev, &np->napi, netdev_poll, max_interrupt_work);
|
||||
|
||||
|
|
|
|||
|
|
@ -476,7 +476,7 @@ static int acenic_probe_one(struct pci_dev *pdev,
|
|||
dev->watchdog_timeo = 5*HZ;
|
||||
|
||||
dev->netdev_ops = &ace_netdev_ops;
|
||||
SET_ETHTOOL_OPS(dev, &ace_ethtool_ops);
|
||||
dev->ethtool_ops = &ace_ethtool_ops;
|
||||
|
||||
/* we only display this string ONCE */
|
||||
if (!boards_found)
|
||||
|
|
|
|||
|
|
@ -353,7 +353,6 @@ static int sgdma_async_read(struct altera_tse_private *priv)
|
|||
|
||||
struct sgdma_descrip __iomem *cdesc = &descbase[0];
|
||||
struct sgdma_descrip __iomem *ndesc = &descbase[1];
|
||||
|
||||
struct tse_buffer *rxbuffer = NULL;
|
||||
|
||||
if (!sgdma_rxbusy(priv)) {
|
||||
|
|
|
|||
|
|
@ -271,5 +271,5 @@ static const struct ethtool_ops tse_ethtool_ops = {
|
|||
|
||||
void altera_tse_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
SET_ETHTOOL_OPS(netdev, &tse_ethtool_ops);
|
||||
netdev->ethtool_ops = &tse_ethtool_ops;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ config NET_VENDOR_AMD
|
|||
default y
|
||||
depends on DIO || MACH_DECSTATION || MVME147 || ATARI || SUN3 || \
|
||||
SUN3X || SBUS || PCI || ZORRO || (ISA && ISA_DMA_API) || \
|
||||
(ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA
|
||||
(ARM && ARCH_EBSA110) || ISA || EISA || PCMCIA || ARM64
|
||||
---help---
|
||||
If you have a network (Ethernet) chipset belonging to this class,
|
||||
say Y.
|
||||
|
|
@ -177,4 +177,16 @@ config SUNLANCE
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called sunlance.
|
||||
|
||||
config AMD_XGBE
|
||||
tristate "AMD 10GbE Ethernet driver"
|
||||
depends on OF_NET
|
||||
select PHYLIB
|
||||
select AMD_XGBE_PHY
|
||||
---help---
|
||||
This driver supports the AMD 10GbE Ethernet device found on an
|
||||
AMD SoC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called amd-xgbe.
|
||||
|
||||
endif # NET_VENDOR_AMD
|
||||
|
|
|
|||
|
|
@ -17,3 +17,4 @@ obj-$(CONFIG_NI65) += ni65.o
|
|||
obj-$(CONFIG_PCNET32) += pcnet32.o
|
||||
obj-$(CONFIG_SUN3LANCE) += sun3lance.o
|
||||
obj-$(CONFIG_SUNLANCE) += sunlance.o
|
||||
obj-$(CONFIG_AMD_XGBE) += xgbe/
|
||||
|
|
|
|||
|
|
@ -1900,7 +1900,7 @@ static int amd8111e_probe_one(struct pci_dev *pdev,
|
|||
|
||||
/* Initialize driver entry points */
|
||||
dev->netdev_ops = &amd8111e_netdev_ops;
|
||||
SET_ETHTOOL_OPS(dev, &ops);
|
||||
dev->ethtool_ops = &ops;
|
||||
dev->irq =pdev->irq;
|
||||
dev->watchdog_timeo = AMD8111E_TX_TIMEOUT;
|
||||
netif_napi_add(dev, &lp->napi, amd8111e_rx_poll, 32);
|
||||
|
|
|
|||
|
|
@ -718,7 +718,6 @@ static int ariadne_init_one(struct zorro_dev *z,
|
|||
unsigned long mem_start = board + ARIADNE_RAM;
|
||||
struct resource *r1, *r2;
|
||||
struct net_device *dev;
|
||||
struct ariadne_private *priv;
|
||||
u32 serial;
|
||||
int err;
|
||||
|
||||
|
|
@ -738,8 +737,6 @@ static int ariadne_init_one(struct zorro_dev *z,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv = netdev_priv(dev);
|
||||
|
||||
r1->name = dev->name;
|
||||
r2->name = dev->name;
|
||||
|
||||
|
|
|
|||
|
|
@ -1229,7 +1229,7 @@ static int au1000_probe(struct platform_device *pdev)
|
|||
dev->base_addr = base->start;
|
||||
dev->irq = irq;
|
||||
dev->netdev_ops = &au1000_netdev_ops;
|
||||
SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
|
||||
dev->ethtool_ops = &au1000_ethtool_ops;
|
||||
dev->watchdog_timeo = ETH_TX_TIMEOUT;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@
|
|||
|
||||
#include "hplance.h"
|
||||
|
||||
/* We have 16834 bytes of RAM for the init block and buffers. This places
|
||||
/* We have 16392 bytes of RAM for the init block and buffers. This places
|
||||
* an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
|
||||
* buffers and 2 Tx buffers.
|
||||
* buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
|
||||
*/
|
||||
#define LANCE_LOG_TX_BUFFERS 1
|
||||
#define LANCE_LOG_RX_BUFFERS 3
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@
|
|||
#include <asm/pgtable.h>
|
||||
#include <asm/mvme147hw.h>
|
||||
|
||||
/* We have 16834 bytes of RAM for the init block and buffers. This places
|
||||
/* We have 32K of RAM for the init block and buffers. This places
|
||||
* an upper limit on the number of buffers we can use. NetBSD uses 8 Rx
|
||||
* buffers and 2 Tx buffers.
|
||||
* buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes.
|
||||
*/
|
||||
#define LANCE_LOG_TX_BUFFERS 1
|
||||
#define LANCE_LOG_RX_BUFFERS 3
|
||||
|
|
@ -111,7 +111,7 @@ struct net_device * __init mvme147lance_probe(int unit)
|
|||
dev->dev_addr);
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
lp->ram = __get_dma_pages(GFP_ATOMIC, 3); /* 16K */
|
||||
lp->ram = __get_dma_pages(GFP_ATOMIC, 3); /* 32K */
|
||||
if (!lp->ram) {
|
||||
printk("%s: No memory for LANCE buffers\n", dev->name);
|
||||
free_netdev(dev);
|
||||
|
|
|
|||
|
|
@ -457,7 +457,7 @@ static int nmclan_probe(struct pcmcia_device *link)
|
|||
lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
|
||||
|
||||
dev->netdev_ops = &mace_netdev_ops;
|
||||
SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
|
||||
dev->ethtool_ops = &netdev_ethtool_ops;
|
||||
dev->watchdog_timeo = TX_TIMEOUT;
|
||||
|
||||
return nmclan_config(link);
|
||||
|
|
|
|||
6
drivers/net/ethernet/amd/xgbe/Makefile
Normal file
6
drivers/net/ethernet/amd/xgbe/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
|
||||
|
||||
amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
|
||||
xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o
|
||||
|
||||
amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o
|
||||
1007
drivers/net/ethernet/amd/xgbe/xgbe-common.h
Normal file
1007
drivers/net/ethernet/amd/xgbe/xgbe-common.h
Normal file
File diff suppressed because it is too large
Load diff
375
drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
Normal file
375
drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* AMD 10Gb Ethernet driver
|
||||
*
|
||||
* This file is available to you under your choice of the following two
|
||||
* licenses:
|
||||
*
|
||||
* License 1: GPLv2
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This file is free software; you may copy, redistribute 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 file 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/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* License 2: Modified BSD
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Advanced Micro Devices, Inc. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "xgbe.h"
|
||||
#include "xgbe-common.h"
|
||||
|
||||
|
||||
static ssize_t xgbe_common_read(char __user *buffer, size_t count,
|
||||
loff_t *ppos, unsigned int value)
|
||||
{
|
||||
char *buf;
|
||||
ssize_t len;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
buf = kasprintf(GFP_KERNEL, "0x%08x\n", value);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (count < strlen(buf)) {
|
||||
kfree(buf);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
|
||||
kfree(buf);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t xgbe_common_write(const char __user *buffer, size_t count,
|
||||
loff_t *ppos, unsigned int *value)
|
||||
{
|
||||
char workarea[32];
|
||||
ssize_t len;
|
||||
unsigned int scan_value;
|
||||
|
||||
if (*ppos != 0)
|
||||
return 0;
|
||||
|
||||
if (count >= sizeof(workarea))
|
||||
return -ENOSPC;
|
||||
|
||||
len = simple_write_to_buffer(workarea, sizeof(workarea) - 1, ppos,
|
||||
buffer, count);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
workarea[len] = '\0';
|
||||
if (sscanf(workarea, "%x", &scan_value) == 1)
|
||||
*value = scan_value;
|
||||
else
|
||||
return -EIO;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t xgmac_reg_addr_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
|
||||
return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xgmac_reg);
|
||||
}
|
||||
|
||||
static ssize_t xgmac_reg_addr_write(struct file *filp,
|
||||
const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
|
||||
return xgbe_common_write(buffer, count, ppos,
|
||||
&pdata->debugfs_xgmac_reg);
|
||||
}
|
||||
|
||||
static ssize_t xgmac_reg_value_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
unsigned int value;
|
||||
|
||||
value = XGMAC_IOREAD(pdata, pdata->debugfs_xgmac_reg);
|
||||
|
||||
return xgbe_common_read(buffer, count, ppos, value);
|
||||
}
|
||||
|
||||
static ssize_t xgmac_reg_value_write(struct file *filp,
|
||||
const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
unsigned int value;
|
||||
ssize_t len;
|
||||
|
||||
len = xgbe_common_write(buffer, count, ppos, &value);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
XGMAC_IOWRITE(pdata, pdata->debugfs_xgmac_reg, value);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations xgmac_reg_addr_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = xgmac_reg_addr_read,
|
||||
.write = xgmac_reg_addr_write,
|
||||
};
|
||||
|
||||
static const struct file_operations xgmac_reg_value_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = xgmac_reg_value_read,
|
||||
.write = xgmac_reg_value_write,
|
||||
};
|
||||
|
||||
static ssize_t xpcs_mmd_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
|
||||
return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_mmd);
|
||||
}
|
||||
|
||||
static ssize_t xpcs_mmd_write(struct file *filp, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
|
||||
return xgbe_common_write(buffer, count, ppos,
|
||||
&pdata->debugfs_xpcs_mmd);
|
||||
}
|
||||
|
||||
static ssize_t xpcs_reg_addr_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
|
||||
return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xpcs_reg);
|
||||
}
|
||||
|
||||
static ssize_t xpcs_reg_addr_write(struct file *filp, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
|
||||
return xgbe_common_write(buffer, count, ppos,
|
||||
&pdata->debugfs_xpcs_reg);
|
||||
}
|
||||
|
||||
static ssize_t xpcs_reg_value_read(struct file *filp, char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
unsigned int value;
|
||||
|
||||
value = pdata->hw_if.read_mmd_regs(pdata, pdata->debugfs_xpcs_mmd,
|
||||
pdata->debugfs_xpcs_reg);
|
||||
|
||||
return xgbe_common_read(buffer, count, ppos, value);
|
||||
}
|
||||
|
||||
static ssize_t xpcs_reg_value_write(struct file *filp,
|
||||
const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = filp->private_data;
|
||||
unsigned int value;
|
||||
ssize_t len;
|
||||
|
||||
len = xgbe_common_write(buffer, count, ppos, &value);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
pdata->hw_if.write_mmd_regs(pdata, pdata->debugfs_xpcs_mmd,
|
||||
pdata->debugfs_xpcs_reg, value);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct file_operations xpcs_mmd_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = xpcs_mmd_read,
|
||||
.write = xpcs_mmd_write,
|
||||
};
|
||||
|
||||
static const struct file_operations xpcs_reg_addr_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = xpcs_reg_addr_read,
|
||||
.write = xpcs_reg_addr_write,
|
||||
};
|
||||
|
||||
static const struct file_operations xpcs_reg_value_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = simple_open,
|
||||
.read = xpcs_reg_value_read,
|
||||
.write = xpcs_reg_value_write,
|
||||
};
|
||||
|
||||
void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct dentry *pfile;
|
||||
char *buf;
|
||||
|
||||
/* Set defaults */
|
||||
pdata->debugfs_xgmac_reg = 0;
|
||||
pdata->debugfs_xpcs_mmd = 1;
|
||||
pdata->debugfs_xpcs_reg = 0;
|
||||
|
||||
buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
|
||||
pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL);
|
||||
if (pdata->xgbe_debugfs == NULL) {
|
||||
netdev_err(pdata->netdev, "debugfs_create_dir failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pfile = debugfs_create_file("xgmac_register", 0600,
|
||||
pdata->xgbe_debugfs, pdata,
|
||||
&xgmac_reg_addr_fops);
|
||||
if (!pfile)
|
||||
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
|
||||
|
||||
pfile = debugfs_create_file("xgmac_register_value", 0600,
|
||||
pdata->xgbe_debugfs, pdata,
|
||||
&xgmac_reg_value_fops);
|
||||
if (!pfile)
|
||||
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
|
||||
|
||||
pfile = debugfs_create_file("xpcs_mmd", 0600,
|
||||
pdata->xgbe_debugfs, pdata,
|
||||
&xpcs_mmd_fops);
|
||||
if (!pfile)
|
||||
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
|
||||
|
||||
pfile = debugfs_create_file("xpcs_register", 0600,
|
||||
pdata->xgbe_debugfs, pdata,
|
||||
&xpcs_reg_addr_fops);
|
||||
if (!pfile)
|
||||
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
|
||||
|
||||
pfile = debugfs_create_file("xpcs_register_value", 0600,
|
||||
pdata->xgbe_debugfs, pdata,
|
||||
&xpcs_reg_value_fops);
|
||||
if (!pfile)
|
||||
netdev_err(pdata->netdev, "debugfs_create_file failed\n");
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
void xgbe_debugfs_exit(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
debugfs_remove_recursive(pdata->xgbe_debugfs);
|
||||
pdata->xgbe_debugfs = NULL;
|
||||
}
|
||||
556
drivers/net/ethernet/amd/xgbe/xgbe-desc.c
Normal file
556
drivers/net/ethernet/amd/xgbe/xgbe-desc.c
Normal file
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
* AMD 10Gb Ethernet driver
|
||||
*
|
||||
* This file is available to you under your choice of the following two
|
||||
* licenses:
|
||||
*
|
||||
* License 1: GPLv2
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This file is free software; you may copy, redistribute 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 file 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/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* License 2: Modified BSD
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Advanced Micro Devices, Inc. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "xgbe.h"
|
||||
#include "xgbe-common.h"
|
||||
|
||||
|
||||
static void xgbe_unmap_skb(struct xgbe_prv_data *, struct xgbe_ring_data *);
|
||||
|
||||
static void xgbe_free_ring(struct xgbe_prv_data *pdata,
|
||||
struct xgbe_ring *ring)
|
||||
{
|
||||
struct xgbe_ring_data *rdata;
|
||||
unsigned int i;
|
||||
|
||||
if (!ring)
|
||||
return;
|
||||
|
||||
if (ring->rdata) {
|
||||
for (i = 0; i < ring->rdesc_count; i++) {
|
||||
rdata = GET_DESC_DATA(ring, i);
|
||||
xgbe_unmap_skb(pdata, rdata);
|
||||
}
|
||||
|
||||
kfree(ring->rdata);
|
||||
ring->rdata = NULL;
|
||||
}
|
||||
|
||||
if (ring->rdesc) {
|
||||
dma_free_coherent(pdata->dev,
|
||||
(sizeof(struct xgbe_ring_desc) *
|
||||
ring->rdesc_count),
|
||||
ring->rdesc, ring->rdesc_dma);
|
||||
ring->rdesc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void xgbe_free_ring_resources(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_channel *channel;
|
||||
unsigned int i;
|
||||
|
||||
DBGPR("-->xgbe_free_ring_resources\n");
|
||||
|
||||
channel = pdata->channel;
|
||||
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
||||
xgbe_free_ring(pdata, channel->tx_ring);
|
||||
xgbe_free_ring(pdata, channel->rx_ring);
|
||||
}
|
||||
|
||||
DBGPR("<--xgbe_free_ring_resources\n");
|
||||
}
|
||||
|
||||
static int xgbe_init_ring(struct xgbe_prv_data *pdata,
|
||||
struct xgbe_ring *ring, unsigned int rdesc_count)
|
||||
{
|
||||
DBGPR("-->xgbe_init_ring\n");
|
||||
|
||||
if (!ring)
|
||||
return 0;
|
||||
|
||||
/* Descriptors */
|
||||
ring->rdesc_count = rdesc_count;
|
||||
ring->rdesc = dma_alloc_coherent(pdata->dev,
|
||||
(sizeof(struct xgbe_ring_desc) *
|
||||
rdesc_count), &ring->rdesc_dma,
|
||||
GFP_KERNEL);
|
||||
if (!ring->rdesc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Descriptor information */
|
||||
ring->rdata = kcalloc(rdesc_count, sizeof(struct xgbe_ring_data),
|
||||
GFP_KERNEL);
|
||||
if (!ring->rdata)
|
||||
return -ENOMEM;
|
||||
|
||||
DBGPR(" rdesc=0x%p, rdesc_dma=0x%llx, rdata=0x%p\n",
|
||||
ring->rdesc, ring->rdesc_dma, ring->rdata);
|
||||
|
||||
DBGPR("<--xgbe_init_ring\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgbe_alloc_ring_resources(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_channel *channel;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
DBGPR("-->xgbe_alloc_ring_resources\n");
|
||||
|
||||
channel = pdata->channel;
|
||||
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
||||
DBGPR(" %s - tx_ring:\n", channel->name);
|
||||
ret = xgbe_init_ring(pdata, channel->tx_ring,
|
||||
pdata->tx_desc_count);
|
||||
if (ret) {
|
||||
netdev_alert(pdata->netdev,
|
||||
"error initializing Tx ring\n");
|
||||
goto err_ring;
|
||||
}
|
||||
|
||||
DBGPR(" %s - rx_ring:\n", channel->name);
|
||||
ret = xgbe_init_ring(pdata, channel->rx_ring,
|
||||
pdata->rx_desc_count);
|
||||
if (ret) {
|
||||
netdev_alert(pdata->netdev,
|
||||
"error initializing Tx ring\n");
|
||||
goto err_ring;
|
||||
}
|
||||
}
|
||||
|
||||
DBGPR("<--xgbe_alloc_ring_resources\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_ring:
|
||||
xgbe_free_ring_resources(pdata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xgbe_wrapper_tx_descriptor_init(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
struct xgbe_channel *channel;
|
||||
struct xgbe_ring *ring;
|
||||
struct xgbe_ring_data *rdata;
|
||||
struct xgbe_ring_desc *rdesc;
|
||||
dma_addr_t rdesc_dma;
|
||||
unsigned int i, j;
|
||||
|
||||
DBGPR("-->xgbe_wrapper_tx_descriptor_init\n");
|
||||
|
||||
channel = pdata->channel;
|
||||
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
||||
ring = channel->tx_ring;
|
||||
if (!ring)
|
||||
break;
|
||||
|
||||
rdesc = ring->rdesc;
|
||||
rdesc_dma = ring->rdesc_dma;
|
||||
|
||||
for (j = 0; j < ring->rdesc_count; j++) {
|
||||
rdata = GET_DESC_DATA(ring, j);
|
||||
|
||||
rdata->rdesc = rdesc;
|
||||
rdata->rdesc_dma = rdesc_dma;
|
||||
|
||||
rdesc++;
|
||||
rdesc_dma += sizeof(struct xgbe_ring_desc);
|
||||
}
|
||||
|
||||
ring->cur = 0;
|
||||
ring->dirty = 0;
|
||||
ring->tx.queue_stopped = 0;
|
||||
|
||||
hw_if->tx_desc_init(channel);
|
||||
}
|
||||
|
||||
DBGPR("<--xgbe_wrapper_tx_descriptor_init\n");
|
||||
}
|
||||
|
||||
static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
struct xgbe_channel *channel;
|
||||
struct xgbe_ring *ring;
|
||||
struct xgbe_ring_desc *rdesc;
|
||||
struct xgbe_ring_data *rdata;
|
||||
dma_addr_t rdesc_dma, skb_dma;
|
||||
struct sk_buff *skb = NULL;
|
||||
unsigned int i, j;
|
||||
|
||||
DBGPR("-->xgbe_wrapper_rx_descriptor_init\n");
|
||||
|
||||
channel = pdata->channel;
|
||||
for (i = 0; i < pdata->channel_count; i++, channel++) {
|
||||
ring = channel->rx_ring;
|
||||
if (!ring)
|
||||
break;
|
||||
|
||||
rdesc = ring->rdesc;
|
||||
rdesc_dma = ring->rdesc_dma;
|
||||
|
||||
for (j = 0; j < ring->rdesc_count; j++) {
|
||||
rdata = GET_DESC_DATA(ring, j);
|
||||
|
||||
rdata->rdesc = rdesc;
|
||||
rdata->rdesc_dma = rdesc_dma;
|
||||
|
||||
/* Allocate skb & assign to each rdesc */
|
||||
skb = dev_alloc_skb(pdata->rx_buf_size);
|
||||
if (skb == NULL)
|
||||
break;
|
||||
skb_dma = dma_map_single(pdata->dev, skb->data,
|
||||
pdata->rx_buf_size,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(pdata->dev, skb_dma)) {
|
||||
netdev_alert(pdata->netdev,
|
||||
"failed to do the dma map\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
break;
|
||||
}
|
||||
rdata->skb = skb;
|
||||
rdata->skb_dma = skb_dma;
|
||||
rdata->skb_dma_len = pdata->rx_buf_size;
|
||||
|
||||
rdesc++;
|
||||
rdesc_dma += sizeof(struct xgbe_ring_desc);
|
||||
}
|
||||
|
||||
ring->cur = 0;
|
||||
ring->dirty = 0;
|
||||
ring->rx.realloc_index = 0;
|
||||
ring->rx.realloc_threshold = 0;
|
||||
|
||||
hw_if->rx_desc_init(channel);
|
||||
}
|
||||
|
||||
DBGPR("<--xgbe_wrapper_rx_descriptor_init\n");
|
||||
}
|
||||
|
||||
static void xgbe_unmap_skb(struct xgbe_prv_data *pdata,
|
||||
struct xgbe_ring_data *rdata)
|
||||
{
|
||||
if (rdata->skb_dma) {
|
||||
if (rdata->mapped_as_page) {
|
||||
dma_unmap_page(pdata->dev, rdata->skb_dma,
|
||||
rdata->skb_dma_len, DMA_TO_DEVICE);
|
||||
} else {
|
||||
dma_unmap_single(pdata->dev, rdata->skb_dma,
|
||||
rdata->skb_dma_len, DMA_TO_DEVICE);
|
||||
}
|
||||
rdata->skb_dma = 0;
|
||||
rdata->skb_dma_len = 0;
|
||||
}
|
||||
|
||||
if (rdata->skb) {
|
||||
dev_kfree_skb_any(rdata->skb);
|
||||
rdata->skb = NULL;
|
||||
}
|
||||
|
||||
rdata->tso_header = 0;
|
||||
rdata->len = 0;
|
||||
rdata->interrupt = 0;
|
||||
rdata->mapped_as_page = 0;
|
||||
}
|
||||
|
||||
static int xgbe_map_tx_skb(struct xgbe_channel *channel, struct sk_buff *skb)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = channel->pdata;
|
||||
struct xgbe_ring *ring = channel->tx_ring;
|
||||
struct xgbe_ring_data *rdata;
|
||||
struct xgbe_packet_data *packet;
|
||||
struct skb_frag_struct *frag;
|
||||
dma_addr_t skb_dma;
|
||||
unsigned int start_index, cur_index;
|
||||
unsigned int offset, tso, vlan, datalen, len;
|
||||
unsigned int i;
|
||||
|
||||
DBGPR("-->xgbe_map_tx_skb: cur = %d\n", ring->cur);
|
||||
|
||||
offset = 0;
|
||||
start_index = ring->cur;
|
||||
cur_index = ring->cur;
|
||||
|
||||
packet = &ring->packet_data;
|
||||
packet->rdesc_count = 0;
|
||||
packet->length = 0;
|
||||
|
||||
tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
|
||||
TSO_ENABLE);
|
||||
vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES,
|
||||
VLAN_CTAG);
|
||||
|
||||
/* Save space for a context descriptor if needed */
|
||||
if ((tso && (packet->mss != ring->tx.cur_mss)) ||
|
||||
(vlan && (packet->vlan_ctag != ring->tx.cur_vlan_ctag)))
|
||||
cur_index++;
|
||||
rdata = GET_DESC_DATA(ring, cur_index);
|
||||
|
||||
if (tso) {
|
||||
DBGPR(" TSO packet\n");
|
||||
|
||||
/* Map the TSO header */
|
||||
skb_dma = dma_map_single(pdata->dev, skb->data,
|
||||
packet->header_len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(pdata->dev, skb_dma)) {
|
||||
netdev_alert(pdata->netdev, "dma_map_single failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
rdata->skb_dma = skb_dma;
|
||||
rdata->skb_dma_len = packet->header_len;
|
||||
rdata->tso_header = 1;
|
||||
|
||||
offset = packet->header_len;
|
||||
|
||||
packet->length += packet->header_len;
|
||||
|
||||
cur_index++;
|
||||
rdata = GET_DESC_DATA(ring, cur_index);
|
||||
}
|
||||
|
||||
/* Map the (remainder of the) packet */
|
||||
for (datalen = skb_headlen(skb) - offset; datalen; ) {
|
||||
len = min_t(unsigned int, datalen, TX_MAX_BUF_SIZE);
|
||||
|
||||
skb_dma = dma_map_single(pdata->dev, skb->data + offset, len,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(pdata->dev, skb_dma)) {
|
||||
netdev_alert(pdata->netdev, "dma_map_single failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
rdata->skb_dma = skb_dma;
|
||||
rdata->skb_dma_len = len;
|
||||
DBGPR(" skb data: index=%u, dma=0x%llx, len=%u\n",
|
||||
cur_index, skb_dma, len);
|
||||
|
||||
datalen -= len;
|
||||
offset += len;
|
||||
|
||||
packet->length += len;
|
||||
|
||||
cur_index++;
|
||||
rdata = GET_DESC_DATA(ring, cur_index);
|
||||
}
|
||||
|
||||
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
|
||||
DBGPR(" mapping frag %u\n", i);
|
||||
|
||||
frag = &skb_shinfo(skb)->frags[i];
|
||||
offset = 0;
|
||||
|
||||
for (datalen = skb_frag_size(frag); datalen; ) {
|
||||
len = min_t(unsigned int, datalen, TX_MAX_BUF_SIZE);
|
||||
|
||||
skb_dma = skb_frag_dma_map(pdata->dev, frag, offset,
|
||||
len, DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(pdata->dev, skb_dma)) {
|
||||
netdev_alert(pdata->netdev,
|
||||
"skb_frag_dma_map failed\n");
|
||||
goto err_out;
|
||||
}
|
||||
rdata->skb_dma = skb_dma;
|
||||
rdata->skb_dma_len = len;
|
||||
rdata->mapped_as_page = 1;
|
||||
DBGPR(" skb data: index=%u, dma=0x%llx, len=%u\n",
|
||||
cur_index, skb_dma, len);
|
||||
|
||||
datalen -= len;
|
||||
offset += len;
|
||||
|
||||
packet->length += len;
|
||||
|
||||
cur_index++;
|
||||
rdata = GET_DESC_DATA(ring, cur_index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the skb address in the last entry */
|
||||
rdata->skb = skb;
|
||||
|
||||
/* Save the number of descriptor entries used */
|
||||
packet->rdesc_count = cur_index - start_index;
|
||||
|
||||
DBGPR("<--xgbe_map_tx_skb: count=%u\n", packet->rdesc_count);
|
||||
|
||||
return packet->rdesc_count;
|
||||
|
||||
err_out:
|
||||
while (start_index < cur_index) {
|
||||
rdata = GET_DESC_DATA(ring, start_index++);
|
||||
xgbe_unmap_skb(pdata, rdata);
|
||||
}
|
||||
|
||||
DBGPR("<--xgbe_map_tx_skb: count=0\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xgbe_realloc_skb(struct xgbe_channel *channel)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = channel->pdata;
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
struct xgbe_ring *ring = channel->rx_ring;
|
||||
struct xgbe_ring_data *rdata;
|
||||
struct sk_buff *skb = NULL;
|
||||
dma_addr_t skb_dma;
|
||||
int i;
|
||||
|
||||
DBGPR("-->xgbe_realloc_skb: rx_ring->rx.realloc_index = %u\n",
|
||||
ring->rx.realloc_index);
|
||||
|
||||
for (i = 0; i < ring->dirty; i++) {
|
||||
rdata = GET_DESC_DATA(ring, ring->rx.realloc_index);
|
||||
|
||||
/* Reset rdata values */
|
||||
xgbe_unmap_skb(pdata, rdata);
|
||||
|
||||
/* Allocate skb & assign to each rdesc */
|
||||
skb = dev_alloc_skb(pdata->rx_buf_size);
|
||||
if (skb == NULL) {
|
||||
netdev_alert(pdata->netdev,
|
||||
"failed to allocate skb\n");
|
||||
break;
|
||||
}
|
||||
skb_dma = dma_map_single(pdata->dev, skb->data,
|
||||
pdata->rx_buf_size, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(pdata->dev, skb_dma)) {
|
||||
netdev_alert(pdata->netdev,
|
||||
"failed to do the dma map\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
break;
|
||||
}
|
||||
rdata->skb = skb;
|
||||
rdata->skb_dma = skb_dma;
|
||||
rdata->skb_dma_len = pdata->rx_buf_size;
|
||||
|
||||
hw_if->rx_desc_reset(rdata);
|
||||
|
||||
ring->rx.realloc_index++;
|
||||
}
|
||||
ring->dirty = 0;
|
||||
|
||||
DBGPR("<--xgbe_realloc_skb\n");
|
||||
}
|
||||
|
||||
void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if)
|
||||
{
|
||||
DBGPR("-->xgbe_init_function_ptrs_desc\n");
|
||||
|
||||
desc_if->alloc_ring_resources = xgbe_alloc_ring_resources;
|
||||
desc_if->free_ring_resources = xgbe_free_ring_resources;
|
||||
desc_if->map_tx_skb = xgbe_map_tx_skb;
|
||||
desc_if->realloc_skb = xgbe_realloc_skb;
|
||||
desc_if->unmap_skb = xgbe_unmap_skb;
|
||||
desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init;
|
||||
desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init;
|
||||
|
||||
DBGPR("<--xgbe_init_function_ptrs_desc\n");
|
||||
}
|
||||
2182
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
Normal file
2182
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
Normal file
File diff suppressed because it is too large
Load diff
1351
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
Normal file
1351
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
Normal file
File diff suppressed because it is too large
Load diff
510
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
Normal file
510
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
Normal file
|
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
* AMD 10Gb Ethernet driver
|
||||
*
|
||||
* This file is available to you under your choice of the following two
|
||||
* licenses:
|
||||
*
|
||||
* License 1: GPLv2
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This file is free software; you may copy, redistribute 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 file 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/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* License 2: Modified BSD
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Advanced Micro Devices, Inc. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#include "xgbe.h"
|
||||
#include "xgbe-common.h"
|
||||
|
||||
|
||||
struct xgbe_stats {
|
||||
char stat_string[ETH_GSTRING_LEN];
|
||||
int stat_size;
|
||||
int stat_offset;
|
||||
};
|
||||
|
||||
#define XGMAC_MMC_STAT(_string, _var) \
|
||||
{ _string, \
|
||||
FIELD_SIZEOF(struct xgbe_mmc_stats, _var), \
|
||||
offsetof(struct xgbe_prv_data, mmc_stats._var), \
|
||||
}
|
||||
|
||||
static const struct xgbe_stats xgbe_gstring_stats[] = {
|
||||
XGMAC_MMC_STAT("tx_bytes", txoctetcount_gb),
|
||||
XGMAC_MMC_STAT("tx_packets", txframecount_gb),
|
||||
XGMAC_MMC_STAT("tx_unicast_packets", txunicastframes_gb),
|
||||
XGMAC_MMC_STAT("tx_broadcast_packets", txbroadcastframes_gb),
|
||||
XGMAC_MMC_STAT("tx_multicast_packets", txmulticastframes_gb),
|
||||
XGMAC_MMC_STAT("tx_vlan_packets", txvlanframes_g),
|
||||
XGMAC_MMC_STAT("tx_64_byte_packets", tx64octets_gb),
|
||||
XGMAC_MMC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb),
|
||||
XGMAC_MMC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb),
|
||||
XGMAC_MMC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb),
|
||||
XGMAC_MMC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb),
|
||||
XGMAC_MMC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb),
|
||||
XGMAC_MMC_STAT("tx_underflow_errors", txunderflowerror),
|
||||
XGMAC_MMC_STAT("tx_pause_frames", txpauseframes),
|
||||
|
||||
XGMAC_MMC_STAT("rx_bytes", rxoctetcount_gb),
|
||||
XGMAC_MMC_STAT("rx_packets", rxframecount_gb),
|
||||
XGMAC_MMC_STAT("rx_unicast_packets", rxunicastframes_g),
|
||||
XGMAC_MMC_STAT("rx_broadcast_packets", rxbroadcastframes_g),
|
||||
XGMAC_MMC_STAT("rx_multicast_packets", rxmulticastframes_g),
|
||||
XGMAC_MMC_STAT("rx_vlan_packets", rxvlanframes_gb),
|
||||
XGMAC_MMC_STAT("rx_64_byte_packets", rx64octets_gb),
|
||||
XGMAC_MMC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb),
|
||||
XGMAC_MMC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb),
|
||||
XGMAC_MMC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb),
|
||||
XGMAC_MMC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb),
|
||||
XGMAC_MMC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb),
|
||||
XGMAC_MMC_STAT("rx_undersize_packets", rxundersize_g),
|
||||
XGMAC_MMC_STAT("rx_oversize_packets", rxoversize_g),
|
||||
XGMAC_MMC_STAT("rx_crc_errors", rxcrcerror),
|
||||
XGMAC_MMC_STAT("rx_crc_errors_small_packets", rxrunterror),
|
||||
XGMAC_MMC_STAT("rx_crc_errors_giant_packets", rxjabbererror),
|
||||
XGMAC_MMC_STAT("rx_length_errors", rxlengtherror),
|
||||
XGMAC_MMC_STAT("rx_out_of_range_errors", rxoutofrangetype),
|
||||
XGMAC_MMC_STAT("rx_fifo_overflow_errors", rxfifooverflow),
|
||||
XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror),
|
||||
XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes),
|
||||
};
|
||||
#define XGBE_STATS_COUNT ARRAY_SIZE(xgbe_gstring_stats)
|
||||
|
||||
static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
DBGPR("-->%s\n", __func__);
|
||||
|
||||
switch (stringset) {
|
||||
case ETH_SS_STATS:
|
||||
for (i = 0; i < XGBE_STATS_COUNT; i++) {
|
||||
memcpy(data, xgbe_gstring_stats[i].stat_string,
|
||||
ETH_GSTRING_LEN);
|
||||
data += ETH_GSTRING_LEN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
DBGPR("<--%s\n", __func__);
|
||||
}
|
||||
|
||||
static void xgbe_get_ethtool_stats(struct net_device *netdev,
|
||||
struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
u8 *stat;
|
||||
int i;
|
||||
|
||||
DBGPR("-->%s\n", __func__);
|
||||
|
||||
pdata->hw_if.read_mmc_stats(pdata);
|
||||
for (i = 0; i < XGBE_STATS_COUNT; i++) {
|
||||
stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset;
|
||||
*data++ = *(u64 *)stat;
|
||||
}
|
||||
|
||||
DBGPR("<--%s\n", __func__);
|
||||
}
|
||||
|
||||
static int xgbe_get_sset_count(struct net_device *netdev, int stringset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DBGPR("-->%s\n", __func__);
|
||||
|
||||
switch (stringset) {
|
||||
case ETH_SS_STATS:
|
||||
ret = XGBE_STATS_COUNT;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
DBGPR("<--%s\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xgbe_get_pauseparam(struct net_device *netdev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
|
||||
DBGPR("-->xgbe_get_pauseparam\n");
|
||||
|
||||
pause->autoneg = pdata->pause_autoneg;
|
||||
pause->tx_pause = pdata->tx_pause;
|
||||
pause->rx_pause = pdata->rx_pause;
|
||||
|
||||
DBGPR("<--xgbe_get_pauseparam\n");
|
||||
}
|
||||
|
||||
static int xgbe_set_pauseparam(struct net_device *netdev,
|
||||
struct ethtool_pauseparam *pause)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
struct phy_device *phydev = pdata->phydev;
|
||||
int ret = 0;
|
||||
|
||||
DBGPR("-->xgbe_set_pauseparam\n");
|
||||
|
||||
DBGPR(" autoneg = %d, tx_pause = %d, rx_pause = %d\n",
|
||||
pause->autoneg, pause->tx_pause, pause->rx_pause);
|
||||
|
||||
pdata->pause_autoneg = pause->autoneg;
|
||||
if (pause->autoneg) {
|
||||
phydev->advertising |= ADVERTISED_Pause;
|
||||
phydev->advertising |= ADVERTISED_Asym_Pause;
|
||||
|
||||
} else {
|
||||
phydev->advertising &= ~ADVERTISED_Pause;
|
||||
phydev->advertising &= ~ADVERTISED_Asym_Pause;
|
||||
|
||||
pdata->tx_pause = pause->tx_pause;
|
||||
pdata->rx_pause = pause->rx_pause;
|
||||
}
|
||||
|
||||
if (netif_running(netdev))
|
||||
ret = phy_start_aneg(phydev);
|
||||
|
||||
DBGPR("<--xgbe_set_pauseparam\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xgbe_get_settings(struct net_device *netdev,
|
||||
struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
int ret;
|
||||
|
||||
DBGPR("-->xgbe_get_settings\n");
|
||||
|
||||
if (!pdata->phydev)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irq(&pdata->lock);
|
||||
|
||||
ret = phy_ethtool_gset(pdata->phydev, cmd);
|
||||
cmd->transceiver = XCVR_EXTERNAL;
|
||||
|
||||
spin_unlock_irq(&pdata->lock);
|
||||
|
||||
DBGPR("<--xgbe_get_settings\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xgbe_set_settings(struct net_device *netdev,
|
||||
struct ethtool_cmd *cmd)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
struct phy_device *phydev = pdata->phydev;
|
||||
u32 speed;
|
||||
int ret;
|
||||
|
||||
DBGPR("-->xgbe_set_settings\n");
|
||||
|
||||
if (!pdata->phydev)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irq(&pdata->lock);
|
||||
|
||||
speed = ethtool_cmd_speed(cmd);
|
||||
|
||||
ret = -EINVAL;
|
||||
if (cmd->phy_address != phydev->addr)
|
||||
goto unlock;
|
||||
|
||||
if ((cmd->autoneg != AUTONEG_ENABLE) &&
|
||||
(cmd->autoneg != AUTONEG_DISABLE))
|
||||
goto unlock;
|
||||
|
||||
if ((cmd->autoneg == AUTONEG_DISABLE) &&
|
||||
(((speed != SPEED_10000) && (speed != SPEED_1000)) ||
|
||||
(cmd->duplex != DUPLEX_FULL)))
|
||||
goto unlock;
|
||||
|
||||
if (cmd->autoneg == AUTONEG_ENABLE) {
|
||||
/* Clear settings needed to force speeds */
|
||||
phydev->supported &= ~SUPPORTED_1000baseT_Full;
|
||||
phydev->supported &= ~SUPPORTED_10000baseT_Full;
|
||||
} else {
|
||||
/* Add settings needed to force speed */
|
||||
phydev->supported |= SUPPORTED_1000baseT_Full;
|
||||
phydev->supported |= SUPPORTED_10000baseT_Full;
|
||||
}
|
||||
|
||||
cmd->advertising &= phydev->supported;
|
||||
if ((cmd->autoneg == AUTONEG_ENABLE) && !cmd->advertising)
|
||||
goto unlock;
|
||||
|
||||
ret = 0;
|
||||
phydev->autoneg = cmd->autoneg;
|
||||
phydev->speed = speed;
|
||||
phydev->duplex = cmd->duplex;
|
||||
phydev->advertising = cmd->advertising;
|
||||
|
||||
if (cmd->autoneg == AUTONEG_ENABLE)
|
||||
phydev->advertising |= ADVERTISED_Autoneg;
|
||||
else
|
||||
phydev->advertising &= ~ADVERTISED_Autoneg;
|
||||
|
||||
if (netif_running(netdev))
|
||||
ret = phy_start_aneg(phydev);
|
||||
|
||||
unlock:
|
||||
spin_unlock_irq(&pdata->lock);
|
||||
|
||||
DBGPR("<--xgbe_set_settings\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xgbe_get_drvinfo(struct net_device *netdev,
|
||||
struct ethtool_drvinfo *drvinfo)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
|
||||
strlcpy(drvinfo->driver, XGBE_DRV_NAME, sizeof(drvinfo->driver));
|
||||
strlcpy(drvinfo->version, XGBE_DRV_VERSION, sizeof(drvinfo->version));
|
||||
strlcpy(drvinfo->bus_info, dev_name(pdata->dev),
|
||||
sizeof(drvinfo->bus_info));
|
||||
snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "%d.%d.%d",
|
||||
XGMAC_IOREAD_BITS(pdata, MAC_VR, USERVER),
|
||||
XGMAC_IOREAD_BITS(pdata, MAC_VR, DEVID),
|
||||
XGMAC_IOREAD_BITS(pdata, MAC_VR, SNPSVER));
|
||||
drvinfo->n_stats = XGBE_STATS_COUNT;
|
||||
}
|
||||
|
||||
static int xgbe_get_coalesce(struct net_device *netdev,
|
||||
struct ethtool_coalesce *ec)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
unsigned int riwt;
|
||||
|
||||
DBGPR("-->xgbe_get_coalesce\n");
|
||||
|
||||
memset(ec, 0, sizeof(struct ethtool_coalesce));
|
||||
|
||||
riwt = pdata->rx_riwt;
|
||||
ec->rx_coalesce_usecs = hw_if->riwt_to_usec(pdata, riwt);
|
||||
ec->rx_max_coalesced_frames = pdata->rx_frames;
|
||||
|
||||
ec->tx_coalesce_usecs = pdata->tx_usecs;
|
||||
ec->tx_max_coalesced_frames = pdata->tx_frames;
|
||||
|
||||
DBGPR("<--xgbe_get_coalesce\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgbe_set_coalesce(struct net_device *netdev,
|
||||
struct ethtool_coalesce *ec)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
unsigned int rx_frames, rx_riwt, rx_usecs;
|
||||
unsigned int tx_frames, tx_usecs;
|
||||
|
||||
DBGPR("-->xgbe_set_coalesce\n");
|
||||
|
||||
/* Check for not supported parameters */
|
||||
if ((ec->rx_coalesce_usecs_irq) ||
|
||||
(ec->rx_max_coalesced_frames_irq) ||
|
||||
(ec->tx_coalesce_usecs_irq) ||
|
||||
(ec->tx_max_coalesced_frames_irq) ||
|
||||
(ec->stats_block_coalesce_usecs) ||
|
||||
(ec->use_adaptive_rx_coalesce) ||
|
||||
(ec->use_adaptive_tx_coalesce) ||
|
||||
(ec->pkt_rate_low) ||
|
||||
(ec->rx_coalesce_usecs_low) ||
|
||||
(ec->rx_max_coalesced_frames_low) ||
|
||||
(ec->tx_coalesce_usecs_low) ||
|
||||
(ec->tx_max_coalesced_frames_low) ||
|
||||
(ec->pkt_rate_high) ||
|
||||
(ec->rx_coalesce_usecs_high) ||
|
||||
(ec->rx_max_coalesced_frames_high) ||
|
||||
(ec->tx_coalesce_usecs_high) ||
|
||||
(ec->tx_max_coalesced_frames_high) ||
|
||||
(ec->rate_sample_interval))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Can only change rx-frames when interface is down (see
|
||||
* rx_descriptor_init in xgbe-dev.c)
|
||||
*/
|
||||
rx_frames = pdata->rx_frames;
|
||||
if (rx_frames != ec->rx_max_coalesced_frames && netif_running(netdev)) {
|
||||
netdev_alert(netdev,
|
||||
"interface must be down to change rx-frames\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rx_riwt = hw_if->usec_to_riwt(pdata, ec->rx_coalesce_usecs);
|
||||
rx_frames = ec->rx_max_coalesced_frames;
|
||||
|
||||
/* Use smallest possible value if conversion resulted in zero */
|
||||
if (ec->rx_coalesce_usecs && !rx_riwt)
|
||||
rx_riwt = 1;
|
||||
|
||||
/* Check the bounds of values for Rx */
|
||||
if (rx_riwt > XGMAC_MAX_DMA_RIWT) {
|
||||
rx_usecs = hw_if->riwt_to_usec(pdata, XGMAC_MAX_DMA_RIWT);
|
||||
netdev_alert(netdev, "rx-usec is limited to %d usecs\n",
|
||||
rx_usecs);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (rx_frames > pdata->channel->rx_ring->rdesc_count) {
|
||||
netdev_alert(netdev, "rx-frames is limited to %d frames\n",
|
||||
pdata->channel->rx_ring->rdesc_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tx_usecs = ec->tx_coalesce_usecs;
|
||||
tx_frames = ec->tx_max_coalesced_frames;
|
||||
|
||||
/* Check the bounds of values for Tx */
|
||||
if (tx_frames > pdata->channel->tx_ring->rdesc_count) {
|
||||
netdev_alert(netdev, "tx-frames is limited to %d frames\n",
|
||||
pdata->channel->tx_ring->rdesc_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pdata->rx_riwt = rx_riwt;
|
||||
pdata->rx_frames = rx_frames;
|
||||
hw_if->config_rx_coalesce(pdata);
|
||||
|
||||
pdata->tx_usecs = tx_usecs;
|
||||
pdata->tx_frames = tx_frames;
|
||||
hw_if->config_tx_coalesce(pdata);
|
||||
|
||||
DBGPR("<--xgbe_set_coalesce\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ethtool_ops xgbe_ethtool_ops = {
|
||||
.get_settings = xgbe_get_settings,
|
||||
.set_settings = xgbe_set_settings,
|
||||
.get_drvinfo = xgbe_get_drvinfo,
|
||||
.get_link = ethtool_op_get_link,
|
||||
.get_coalesce = xgbe_get_coalesce,
|
||||
.set_coalesce = xgbe_set_coalesce,
|
||||
.get_pauseparam = xgbe_get_pauseparam,
|
||||
.set_pauseparam = xgbe_set_pauseparam,
|
||||
.get_strings = xgbe_get_strings,
|
||||
.get_ethtool_stats = xgbe_get_ethtool_stats,
|
||||
.get_sset_count = xgbe_get_sset_count,
|
||||
};
|
||||
|
||||
struct ethtool_ops *xgbe_get_ethtool_ops(void)
|
||||
{
|
||||
return (struct ethtool_ops *)&xgbe_ethtool_ops;
|
||||
}
|
||||
512
drivers/net/ethernet/amd/xgbe/xgbe-main.c
Normal file
512
drivers/net/ethernet/amd/xgbe/xgbe-main.c
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* AMD 10Gb Ethernet driver
|
||||
*
|
||||
* This file is available to you under your choice of the following two
|
||||
* licenses:
|
||||
*
|
||||
* License 1: GPLv2
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This file is free software; you may copy, redistribute 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 file 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/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* License 2: Modified BSD
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Advanced Micro Devices, Inc. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include "xgbe.h"
|
||||
#include "xgbe-common.h"
|
||||
|
||||
|
||||
MODULE_AUTHOR("Tom Lendacky <[email protected]>");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_VERSION(XGBE_DRV_VERSION);
|
||||
MODULE_DESCRIPTION(XGBE_DRV_DESC);
|
||||
|
||||
static struct xgbe_channel *xgbe_alloc_rings(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct xgbe_channel *channel_mem, *channel;
|
||||
struct xgbe_ring *tx_ring, *rx_ring;
|
||||
unsigned int count, i;
|
||||
|
||||
DBGPR("-->xgbe_alloc_rings\n");
|
||||
|
||||
count = max_t(unsigned int, pdata->tx_ring_count, pdata->rx_ring_count);
|
||||
|
||||
channel_mem = devm_kcalloc(pdata->dev, count,
|
||||
sizeof(struct xgbe_channel), GFP_KERNEL);
|
||||
if (!channel_mem)
|
||||
return NULL;
|
||||
|
||||
tx_ring = devm_kcalloc(pdata->dev, pdata->tx_ring_count,
|
||||
sizeof(struct xgbe_ring), GFP_KERNEL);
|
||||
if (!tx_ring)
|
||||
return NULL;
|
||||
|
||||
rx_ring = devm_kcalloc(pdata->dev, pdata->rx_ring_count,
|
||||
sizeof(struct xgbe_ring), GFP_KERNEL);
|
||||
if (!rx_ring)
|
||||
return NULL;
|
||||
|
||||
for (i = 0, channel = channel_mem; i < count; i++, channel++) {
|
||||
snprintf(channel->name, sizeof(channel->name), "channel-%d", i);
|
||||
channel->pdata = pdata;
|
||||
channel->queue_index = i;
|
||||
channel->dma_regs = pdata->xgmac_regs + DMA_CH_BASE +
|
||||
(DMA_CH_INC * i);
|
||||
|
||||
if (i < pdata->tx_ring_count) {
|
||||
spin_lock_init(&tx_ring->lock);
|
||||
channel->tx_ring = tx_ring++;
|
||||
}
|
||||
|
||||
if (i < pdata->rx_ring_count) {
|
||||
spin_lock_init(&tx_ring->lock);
|
||||
channel->rx_ring = rx_ring++;
|
||||
}
|
||||
|
||||
DBGPR(" %s - queue_index=%u, dma_regs=%p, tx=%p, rx=%p\n",
|
||||
channel->name, channel->queue_index, channel->dma_regs,
|
||||
channel->tx_ring, channel->rx_ring);
|
||||
}
|
||||
|
||||
pdata->channel_count = count;
|
||||
|
||||
DBGPR("<--xgbe_alloc_rings\n");
|
||||
|
||||
return channel_mem;
|
||||
}
|
||||
|
||||
static void xgbe_default_config(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
DBGPR("-->xgbe_default_config\n");
|
||||
|
||||
pdata->pblx8 = DMA_PBL_X8_ENABLE;
|
||||
pdata->tx_sf_mode = MTL_TSF_ENABLE;
|
||||
pdata->tx_threshold = MTL_TX_THRESHOLD_64;
|
||||
pdata->tx_pbl = DMA_PBL_16;
|
||||
pdata->tx_osp_mode = DMA_OSP_ENABLE;
|
||||
pdata->rx_sf_mode = MTL_RSF_DISABLE;
|
||||
pdata->rx_threshold = MTL_RX_THRESHOLD_64;
|
||||
pdata->rx_pbl = DMA_PBL_16;
|
||||
pdata->pause_autoneg = 1;
|
||||
pdata->tx_pause = 1;
|
||||
pdata->rx_pause = 1;
|
||||
pdata->power_down = 0;
|
||||
pdata->default_autoneg = AUTONEG_ENABLE;
|
||||
pdata->default_speed = SPEED_10000;
|
||||
|
||||
DBGPR("<--xgbe_default_config\n");
|
||||
}
|
||||
|
||||
static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
xgbe_init_function_ptrs_dev(&pdata->hw_if);
|
||||
xgbe_init_function_ptrs_desc(&pdata->desc_if);
|
||||
}
|
||||
|
||||
static int xgbe_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct xgbe_prv_data *pdata;
|
||||
struct xgbe_hw_if *hw_if;
|
||||
struct xgbe_desc_if *desc_if;
|
||||
struct net_device *netdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
const u8 *mac_addr;
|
||||
int ret;
|
||||
|
||||
DBGPR("--> xgbe_probe\n");
|
||||
|
||||
netdev = alloc_etherdev_mq(sizeof(struct xgbe_prv_data),
|
||||
XGBE_MAX_DMA_CHANNELS);
|
||||
if (!netdev) {
|
||||
dev_err(dev, "alloc_etherdev failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
SET_NETDEV_DEV(netdev, dev);
|
||||
pdata = netdev_priv(netdev);
|
||||
pdata->netdev = netdev;
|
||||
pdata->pdev = pdev;
|
||||
pdata->dev = dev;
|
||||
platform_set_drvdata(pdev, netdev);
|
||||
|
||||
spin_lock_init(&pdata->lock);
|
||||
mutex_init(&pdata->xpcs_mutex);
|
||||
|
||||
/* Set and validate the number of descriptors for a ring */
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(TX_DESC_CNT);
|
||||
pdata->tx_desc_count = TX_DESC_CNT;
|
||||
if (pdata->tx_desc_count & (pdata->tx_desc_count - 1)) {
|
||||
dev_err(dev, "tx descriptor count (%d) is not valid\n",
|
||||
pdata->tx_desc_count);
|
||||
ret = -EINVAL;
|
||||
goto err_io;
|
||||
}
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(RX_DESC_CNT);
|
||||
pdata->rx_desc_count = RX_DESC_CNT;
|
||||
if (pdata->rx_desc_count & (pdata->rx_desc_count - 1)) {
|
||||
dev_err(dev, "rx descriptor count (%d) is not valid\n",
|
||||
pdata->rx_desc_count);
|
||||
ret = -EINVAL;
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
/* Obtain the system clock setting */
|
||||
pdata->sysclock = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(pdata->sysclock)) {
|
||||
dev_err(dev, "devm_clk_get failed\n");
|
||||
ret = PTR_ERR(pdata->sysclock);
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
/* Obtain the mmio areas for the device */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
pdata->xgmac_regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pdata->xgmac_regs)) {
|
||||
dev_err(dev, "xgmac ioremap failed\n");
|
||||
ret = PTR_ERR(pdata->xgmac_regs);
|
||||
goto err_io;
|
||||
}
|
||||
DBGPR(" xgmac_regs = %p\n", pdata->xgmac_regs);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
pdata->xpcs_regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(pdata->xpcs_regs)) {
|
||||
dev_err(dev, "xpcs ioremap failed\n");
|
||||
ret = PTR_ERR(pdata->xpcs_regs);
|
||||
goto err_io;
|
||||
}
|
||||
DBGPR(" xpcs_regs = %p\n", pdata->xpcs_regs);
|
||||
|
||||
/* Set the DMA mask */
|
||||
if (!dev->dma_mask)
|
||||
dev->dma_mask = &dev->coherent_dma_mask;
|
||||
*(dev->dma_mask) = DMA_BIT_MASK(40);
|
||||
dev->coherent_dma_mask = DMA_BIT_MASK(40);
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "platform_get_irq failed\n");
|
||||
goto err_io;
|
||||
}
|
||||
netdev->irq = ret;
|
||||
netdev->base_addr = (unsigned long)pdata->xgmac_regs;
|
||||
|
||||
/* Set all the function pointers */
|
||||
xgbe_init_all_fptrs(pdata);
|
||||
hw_if = &pdata->hw_if;
|
||||
desc_if = &pdata->desc_if;
|
||||
|
||||
/* Issue software reset to device */
|
||||
hw_if->exit(pdata);
|
||||
|
||||
/* Populate the hardware features */
|
||||
xgbe_get_all_hw_features(pdata);
|
||||
|
||||
/* Retrieve the MAC address */
|
||||
mac_addr = of_get_mac_address(dev->of_node);
|
||||
if (!mac_addr) {
|
||||
dev_err(dev, "invalid mac address for this device\n");
|
||||
ret = -EINVAL;
|
||||
goto err_io;
|
||||
}
|
||||
memcpy(netdev->dev_addr, mac_addr, netdev->addr_len);
|
||||
|
||||
/* Retrieve the PHY mode - it must be "xgmii" */
|
||||
pdata->phy_mode = of_get_phy_mode(dev->of_node);
|
||||
if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
|
||||
dev_err(dev, "invalid phy-mode specified for this device\n");
|
||||
ret = -EINVAL;
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
/* Set default configuration data */
|
||||
xgbe_default_config(pdata);
|
||||
|
||||
/* Calculate the number of Tx and Rx rings to be created */
|
||||
pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(),
|
||||
pdata->hw_feat.tx_ch_cnt);
|
||||
if (netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count)) {
|
||||
dev_err(dev, "error setting real tx queue count\n");
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
pdata->rx_ring_count = min_t(unsigned int,
|
||||
netif_get_num_default_rss_queues(),
|
||||
pdata->hw_feat.rx_ch_cnt);
|
||||
ret = netif_set_real_num_rx_queues(netdev, pdata->rx_ring_count);
|
||||
if (ret) {
|
||||
dev_err(dev, "error setting real rx queue count\n");
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
/* Allocate the rings for the DMA channels */
|
||||
pdata->channel = xgbe_alloc_rings(pdata);
|
||||
if (!pdata->channel) {
|
||||
dev_err(dev, "ring allocation failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
/* Prepare to regsiter with MDIO */
|
||||
pdata->mii_bus_id = kasprintf(GFP_KERNEL, "%s", pdev->name);
|
||||
if (!pdata->mii_bus_id) {
|
||||
dev_err(dev, "failed to allocate mii bus id\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_io;
|
||||
}
|
||||
ret = xgbe_mdio_register(pdata);
|
||||
if (ret)
|
||||
goto err_bus_id;
|
||||
|
||||
/* Set network and ethtool operations */
|
||||
netdev->netdev_ops = xgbe_get_netdev_ops();
|
||||
netdev->ethtool_ops = xgbe_get_ethtool_ops();
|
||||
|
||||
/* Set device features */
|
||||
netdev->hw_features = NETIF_F_SG |
|
||||
NETIF_F_IP_CSUM |
|
||||
NETIF_F_IPV6_CSUM |
|
||||
NETIF_F_RXCSUM |
|
||||
NETIF_F_TSO |
|
||||
NETIF_F_TSO6 |
|
||||
NETIF_F_GRO |
|
||||
NETIF_F_HW_VLAN_CTAG_RX |
|
||||
NETIF_F_HW_VLAN_CTAG_TX;
|
||||
|
||||
netdev->vlan_features |= NETIF_F_SG |
|
||||
NETIF_F_IP_CSUM |
|
||||
NETIF_F_IPV6_CSUM |
|
||||
NETIF_F_TSO |
|
||||
NETIF_F_TSO6;
|
||||
|
||||
netdev->features |= netdev->hw_features;
|
||||
pdata->netdev_features = netdev->features;
|
||||
|
||||
xgbe_init_rx_coalesce(pdata);
|
||||
xgbe_init_tx_coalesce(pdata);
|
||||
|
||||
netif_carrier_off(netdev);
|
||||
ret = register_netdev(netdev);
|
||||
if (ret) {
|
||||
dev_err(dev, "net device registration failed\n");
|
||||
goto err_reg_netdev;
|
||||
}
|
||||
|
||||
xgbe_debugfs_init(pdata);
|
||||
|
||||
netdev_notice(netdev, "net device enabled\n");
|
||||
|
||||
DBGPR("<-- xgbe_probe\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_reg_netdev:
|
||||
xgbe_mdio_unregister(pdata);
|
||||
|
||||
err_bus_id:
|
||||
kfree(pdata->mii_bus_id);
|
||||
|
||||
err_io:
|
||||
free_netdev(netdev);
|
||||
|
||||
err_alloc:
|
||||
dev_notice(dev, "net device not enabled\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xgbe_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
|
||||
DBGPR("-->xgbe_remove\n");
|
||||
|
||||
xgbe_debugfs_exit(pdata);
|
||||
|
||||
unregister_netdev(netdev);
|
||||
|
||||
xgbe_mdio_unregister(pdata);
|
||||
|
||||
kfree(pdata->mii_bus_id);
|
||||
|
||||
free_netdev(netdev);
|
||||
|
||||
DBGPR("<--xgbe_remove\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int xgbe_suspend(struct device *dev)
|
||||
{
|
||||
struct net_device *netdev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
DBGPR("-->xgbe_suspend\n");
|
||||
|
||||
if (!netif_running(netdev)) {
|
||||
DBGPR("<--xgbe_dev_suspend\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT);
|
||||
|
||||
DBGPR("<--xgbe_suspend\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xgbe_resume(struct device *dev)
|
||||
{
|
||||
struct net_device *netdev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
DBGPR("-->xgbe_resume\n");
|
||||
|
||||
if (!netif_running(netdev)) {
|
||||
DBGPR("<--xgbe_dev_resume\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT);
|
||||
|
||||
DBGPR("<--xgbe_resume\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct of_device_id xgbe_of_match[] = {
|
||||
{ .compatible = "amd,xgbe-seattle-v1a", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, xgbe_of_match);
|
||||
static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
|
||||
|
||||
static struct platform_driver xgbe_driver = {
|
||||
.driver = {
|
||||
.name = "amd-xgbe",
|
||||
.of_match_table = xgbe_of_match,
|
||||
.pm = &xgbe_pm_ops,
|
||||
},
|
||||
.probe = xgbe_probe,
|
||||
.remove = xgbe_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(xgbe_driver);
|
||||
433
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
Normal file
433
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* AMD 10Gb Ethernet driver
|
||||
*
|
||||
* This file is available to you under your choice of the following two
|
||||
* licenses:
|
||||
*
|
||||
* License 1: GPLv2
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This file is free software; you may copy, redistribute 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 file 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/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* License 2: Modified BSD
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Advanced Micro Devices, Inc. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mdio.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "xgbe.h"
|
||||
#include "xgbe-common.h"
|
||||
|
||||
|
||||
static int xgbe_mdio_read(struct mii_bus *mii, int prtad, int mmd_reg)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = mii->priv;
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
int mmd_data;
|
||||
|
||||
DBGPR_MDIO("-->xgbe_mdio_read: prtad=%#x mmd_reg=%#x\n",
|
||||
prtad, mmd_reg);
|
||||
|
||||
mmd_data = hw_if->read_mmd_regs(pdata, prtad, mmd_reg);
|
||||
|
||||
DBGPR_MDIO("<--xgbe_mdio_read: mmd_data=%#x\n", mmd_data);
|
||||
|
||||
return mmd_data;
|
||||
}
|
||||
|
||||
static int xgbe_mdio_write(struct mii_bus *mii, int prtad, int mmd_reg,
|
||||
u16 mmd_val)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = mii->priv;
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
int mmd_data = mmd_val;
|
||||
|
||||
DBGPR_MDIO("-->xgbe_mdio_write: prtad=%#x mmd_reg=%#x mmd_data=%#x\n",
|
||||
prtad, mmd_reg, mmd_data);
|
||||
|
||||
hw_if->write_mmd_regs(pdata, prtad, mmd_reg, mmd_data);
|
||||
|
||||
DBGPR_MDIO("<--xgbe_mdio_write\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xgbe_adjust_link(struct net_device *netdev)
|
||||
{
|
||||
struct xgbe_prv_data *pdata = netdev_priv(netdev);
|
||||
struct xgbe_hw_if *hw_if = &pdata->hw_if;
|
||||
struct phy_device *phydev = pdata->phydev;
|
||||
unsigned long flags;
|
||||
int new_state = 0;
|
||||
|
||||
if (phydev == NULL)
|
||||
return;
|
||||
|
||||
DBGPR_MDIO("-->xgbe_adjust_link: address=%d, newlink=%d, curlink=%d\n",
|
||||
phydev->addr, phydev->link, pdata->phy_link);
|
||||
|
||||
spin_lock_irqsave(&pdata->lock, flags);
|
||||
|
||||
if (phydev->link) {
|
||||
/* Flow control support */
|
||||
if (pdata->pause_autoneg) {
|
||||
if (phydev->pause || phydev->asym_pause) {
|
||||
pdata->tx_pause = 1;
|
||||
pdata->rx_pause = 1;
|
||||
} else {
|
||||
pdata->tx_pause = 0;
|
||||
pdata->rx_pause = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->tx_pause != pdata->phy_tx_pause) {
|
||||
hw_if->config_tx_flow_control(pdata);
|
||||
pdata->phy_tx_pause = pdata->tx_pause;
|
||||
}
|
||||
|
||||
if (pdata->rx_pause != pdata->phy_rx_pause) {
|
||||
hw_if->config_rx_flow_control(pdata);
|
||||
pdata->phy_rx_pause = pdata->rx_pause;
|
||||
}
|
||||
|
||||
/* Speed support */
|
||||
if (phydev->speed != pdata->phy_speed) {
|
||||
new_state = 1;
|
||||
|
||||
switch (phydev->speed) {
|
||||
case SPEED_10000:
|
||||
hw_if->set_xgmii_speed(pdata);
|
||||
break;
|
||||
|
||||
case SPEED_2500:
|
||||
hw_if->set_gmii_2500_speed(pdata);
|
||||
break;
|
||||
|
||||
case SPEED_1000:
|
||||
hw_if->set_gmii_speed(pdata);
|
||||
break;
|
||||
}
|
||||
pdata->phy_speed = phydev->speed;
|
||||
}
|
||||
|
||||
if (phydev->link != pdata->phy_link) {
|
||||
new_state = 1;
|
||||
pdata->phy_link = 1;
|
||||
}
|
||||
} else if (pdata->phy_link) {
|
||||
new_state = 1;
|
||||
pdata->phy_link = 0;
|
||||
pdata->phy_speed = SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
if (new_state)
|
||||
phy_print_status(phydev);
|
||||
|
||||
spin_unlock_irqrestore(&pdata->lock, flags);
|
||||
|
||||
DBGPR_MDIO("<--xgbe_adjust_link\n");
|
||||
}
|
||||
|
||||
void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct device *dev = pdata->dev;
|
||||
struct phy_device *phydev = pdata->mii->phy_map[XGBE_PRTAD];
|
||||
int i;
|
||||
|
||||
dev_alert(dev, "\n************* PHY Reg dump **********************\n");
|
||||
|
||||
dev_alert(dev, "PCS Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
|
||||
XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1));
|
||||
dev_alert(dev, "PCS Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
|
||||
XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1));
|
||||
dev_alert(dev, "Phy Id (PHYS ID 1 %#04x)= %#04x\n", MDIO_DEVID1,
|
||||
XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID1));
|
||||
dev_alert(dev, "Phy Id (PHYS ID 2 %#04x)= %#04x\n", MDIO_DEVID2,
|
||||
XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVID2));
|
||||
dev_alert(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS1,
|
||||
XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS1));
|
||||
dev_alert(dev, "Devices in Package (%#04x)= %#04x\n", MDIO_DEVS2,
|
||||
XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_DEVS2));
|
||||
|
||||
dev_alert(dev, "Auto-Neg Control Reg (%#04x) = %#04x\n", MDIO_CTRL1,
|
||||
XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1));
|
||||
dev_alert(dev, "Auto-Neg Status Reg (%#04x) = %#04x\n", MDIO_STAT1,
|
||||
XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_STAT1));
|
||||
dev_alert(dev, "Auto-Neg Ad Reg 1 (%#04x) = %#04x\n",
|
||||
MDIO_AN_ADVERTISE,
|
||||
XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE));
|
||||
dev_alert(dev, "Auto-Neg Ad Reg 2 (%#04x) = %#04x\n",
|
||||
MDIO_AN_ADVERTISE + 1,
|
||||
XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1));
|
||||
dev_alert(dev, "Auto-Neg Ad Reg 3 (%#04x) = %#04x\n",
|
||||
MDIO_AN_ADVERTISE + 2,
|
||||
XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2));
|
||||
dev_alert(dev, "Auto-Neg Completion Reg (%#04x) = %#04x\n",
|
||||
MDIO_AN_COMP_STAT,
|
||||
XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_COMP_STAT));
|
||||
|
||||
dev_alert(dev, "MMD Device Mask = %#x\n",
|
||||
phydev->c45_ids.devices_in_package);
|
||||
for (i = 0; i < ARRAY_SIZE(phydev->c45_ids.device_ids); i++)
|
||||
dev_alert(dev, " MMD %d: ID = %#08x\n", i,
|
||||
phydev->c45_ids.device_ids[i]);
|
||||
|
||||
dev_alert(dev, "\n*************************************************\n");
|
||||
}
|
||||
|
||||
int xgbe_mdio_register(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
struct net_device *netdev = pdata->netdev;
|
||||
struct device_node *phy_node;
|
||||
struct mii_bus *mii;
|
||||
struct phy_device *phydev;
|
||||
int ret = 0;
|
||||
|
||||
DBGPR("-->xgbe_mdio_register\n");
|
||||
|
||||
/* Retrieve the phy-handle */
|
||||
phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0);
|
||||
if (!phy_node) {
|
||||
dev_err(pdata->dev, "unable to parse phy-handle\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Register with the MDIO bus */
|
||||
mii = mdiobus_alloc();
|
||||
if (mii == NULL) {
|
||||
dev_err(pdata->dev, "mdiobus_alloc failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_node_get;
|
||||
}
|
||||
|
||||
/* Register on the MDIO bus (don't probe any PHYs) */
|
||||
mii->name = XGBE_PHY_NAME;
|
||||
mii->read = xgbe_mdio_read;
|
||||
mii->write = xgbe_mdio_write;
|
||||
snprintf(mii->id, sizeof(mii->id), "%s", pdata->mii_bus_id);
|
||||
mii->priv = pdata;
|
||||
mii->phy_mask = ~0;
|
||||
mii->parent = pdata->dev;
|
||||
ret = mdiobus_register(mii);
|
||||
if (ret) {
|
||||
dev_err(pdata->dev, "mdiobus_register failed\n");
|
||||
goto err_mdiobus_alloc;
|
||||
}
|
||||
DBGPR(" mdiobus_register succeeded for %s\n", pdata->mii_bus_id);
|
||||
|
||||
/* Probe the PCS using Clause 45 */
|
||||
phydev = get_phy_device(mii, XGBE_PRTAD, true);
|
||||
if (IS_ERR(phydev) || !phydev ||
|
||||
!phydev->c45_ids.device_ids[MDIO_MMD_PCS]) {
|
||||
dev_err(pdata->dev, "get_phy_device failed\n");
|
||||
ret = phydev ? PTR_ERR(phydev) : -ENOLINK;
|
||||
goto err_mdiobus_register;
|
||||
}
|
||||
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
|
||||
MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS]));
|
||||
|
||||
of_node_get(phy_node);
|
||||
phydev->dev.of_node = phy_node;
|
||||
ret = phy_device_register(phydev);
|
||||
if (ret) {
|
||||
dev_err(pdata->dev, "phy_device_register failed\n");
|
||||
of_node_put(phy_node);
|
||||
goto err_phy_device;
|
||||
}
|
||||
|
||||
/* Add a reference to the PHY driver so it can't be unloaded */
|
||||
pdata->phy_module = phydev->dev.driver ?
|
||||
phydev->dev.driver->owner : NULL;
|
||||
if (!try_module_get(pdata->phy_module)) {
|
||||
dev_err(pdata->dev, "try_module_get failed\n");
|
||||
ret = -EIO;
|
||||
goto err_phy_device;
|
||||
}
|
||||
|
||||
pdata->mii = mii;
|
||||
pdata->mdio_mmd = MDIO_MMD_PCS;
|
||||
|
||||
pdata->phy_link = -1;
|
||||
pdata->phy_speed = SPEED_UNKNOWN;
|
||||
pdata->phy_tx_pause = pdata->tx_pause;
|
||||
pdata->phy_rx_pause = pdata->rx_pause;
|
||||
|
||||
ret = phy_connect_direct(netdev, phydev, &xgbe_adjust_link,
|
||||
pdata->phy_mode);
|
||||
if (ret) {
|
||||
netdev_err(netdev, "phy_connect_direct failed\n");
|
||||
goto err_phy_device;
|
||||
}
|
||||
|
||||
if (!phydev->drv || (phydev->drv->phy_id == 0)) {
|
||||
netdev_err(netdev, "phy_id not valid\n");
|
||||
ret = -ENODEV;
|
||||
goto err_phy_connect;
|
||||
}
|
||||
DBGPR(" phy_connect_direct succeeded for PHY %s, link=%d\n",
|
||||
dev_name(&phydev->dev), phydev->link);
|
||||
|
||||
phydev->autoneg = pdata->default_autoneg;
|
||||
if (phydev->autoneg == AUTONEG_DISABLE) {
|
||||
/* Add settings needed to force speed */
|
||||
phydev->supported |= SUPPORTED_1000baseT_Full;
|
||||
phydev->supported |= SUPPORTED_10000baseT_Full;
|
||||
|
||||
phydev->speed = pdata->default_speed;
|
||||
phydev->duplex = DUPLEX_FULL;
|
||||
|
||||
phydev->advertising &= ~ADVERTISED_Autoneg;
|
||||
}
|
||||
|
||||
pdata->phydev = phydev;
|
||||
|
||||
of_node_put(phy_node);
|
||||
|
||||
DBGPHY_REGS(pdata);
|
||||
|
||||
DBGPR("<--xgbe_mdio_register\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_phy_connect:
|
||||
phy_disconnect(phydev);
|
||||
|
||||
err_phy_device:
|
||||
phy_device_free(phydev);
|
||||
|
||||
err_mdiobus_register:
|
||||
mdiobus_unregister(mii);
|
||||
|
||||
err_mdiobus_alloc:
|
||||
mdiobus_free(mii);
|
||||
|
||||
err_node_get:
|
||||
of_node_put(phy_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xgbe_mdio_unregister(struct xgbe_prv_data *pdata)
|
||||
{
|
||||
DBGPR("-->xgbe_mdio_unregister\n");
|
||||
|
||||
phy_disconnect(pdata->phydev);
|
||||
pdata->phydev = NULL;
|
||||
|
||||
module_put(pdata->phy_module);
|
||||
pdata->phy_module = NULL;
|
||||
|
||||
mdiobus_unregister(pdata->mii);
|
||||
pdata->mii->priv = NULL;
|
||||
|
||||
mdiobus_free(pdata->mii);
|
||||
pdata->mii = NULL;
|
||||
|
||||
DBGPR("<--xgbe_mdio_unregister\n");
|
||||
}
|
||||
676
drivers/net/ethernet/amd/xgbe/xgbe.h
Normal file
676
drivers/net/ethernet/amd/xgbe/xgbe.h
Normal file
|
|
@ -0,0 +1,676 @@
|
|||
/*
|
||||
* AMD 10Gb Ethernet driver
|
||||
*
|
||||
* This file is available to you under your choice of the following two
|
||||
* licenses:
|
||||
*
|
||||
* License 1: GPLv2
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This file is free software; you may copy, redistribute 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 file 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/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* License 2: Modified BSD
|
||||
*
|
||||
* Copyright (c) 2014 Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Advanced Micro Devices, Inc. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* The Synopsys DWC ETHER XGMAC Software Driver and documentation
|
||||
* (hereinafter "Software") is an unsupported proprietary work of Synopsys,
|
||||
* Inc. unless otherwise expressly agreed to in writing between Synopsys
|
||||
* and you.
|
||||
*
|
||||
* The Software IS NOT an item of Licensed Software or Licensed Product
|
||||
* under any End User Software License Agreement or Agreement for Licensed
|
||||
* Product with Synopsys or any supplement thereto. Permission is hereby
|
||||
* granted, free of charge, to any person obtaining a copy of this software
|
||||
* annotated with this license and the Software, to deal in the Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished
|
||||
* to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
|
||||
* BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __XGBE_H__
|
||||
#define __XGBE_H__
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
|
||||
#define XGBE_DRV_NAME "amd-xgbe"
|
||||
#define XGBE_DRV_VERSION "1.0.0-a"
|
||||
#define XGBE_DRV_DESC "AMD 10 Gigabit Ethernet Driver"
|
||||
|
||||
/* Descriptor related defines */
|
||||
#define TX_DESC_CNT 512
|
||||
#define TX_DESC_MIN_FREE (TX_DESC_CNT >> 3)
|
||||
#define TX_DESC_MAX_PROC (TX_DESC_CNT >> 1)
|
||||
#define RX_DESC_CNT 512
|
||||
|
||||
#define TX_MAX_BUF_SIZE (0x3fff & ~(64 - 1))
|
||||
|
||||
#define RX_MIN_BUF_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)
|
||||
#define RX_BUF_ALIGN 64
|
||||
|
||||
#define XGBE_MAX_DMA_CHANNELS 16
|
||||
#define DMA_ARDOMAIN_SETTING 0x2
|
||||
#define DMA_ARCACHE_SETTING 0xb
|
||||
#define DMA_AWDOMAIN_SETTING 0x2
|
||||
#define DMA_AWCACHE_SETTING 0x7
|
||||
#define DMA_INTERRUPT_MASK 0x31c7
|
||||
|
||||
#define XGMAC_MIN_PACKET 60
|
||||
#define XGMAC_STD_PACKET_MTU 1500
|
||||
#define XGMAC_MAX_STD_PACKET 1518
|
||||
#define XGMAC_JUMBO_PACKET_MTU 9000
|
||||
#define XGMAC_MAX_JUMBO_PACKET 9018
|
||||
|
||||
#define MAX_MULTICAST_LIST 14
|
||||
#define TX_FLAGS_IP_PKT 0x00000001
|
||||
#define TX_FLAGS_TCP_PKT 0x00000002
|
||||
|
||||
/* MDIO bus phy name */
|
||||
#define XGBE_PHY_NAME "amd_xgbe_phy"
|
||||
#define XGBE_PRTAD 0
|
||||
|
||||
/* Driver PMT macros */
|
||||
#define XGMAC_DRIVER_CONTEXT 1
|
||||
#define XGMAC_IOCTL_CONTEXT 2
|
||||
|
||||
#define FIFO_SIZE_B(x) (x)
|
||||
#define FIFO_SIZE_KB(x) (x * 1024)
|
||||
|
||||
#define XGBE_TC_CNT 2
|
||||
|
||||
/* Helper macro for descriptor handling
|
||||
* Always use GET_DESC_DATA to access the descriptor data
|
||||
* since the index is free-running and needs to be and-ed
|
||||
* with the descriptor count value of the ring to index to
|
||||
* the proper descriptor data.
|
||||
*/
|
||||
#define GET_DESC_DATA(_ring, _idx) \
|
||||
((_ring)->rdata + \
|
||||
((_idx) & ((_ring)->rdesc_count - 1)))
|
||||
|
||||
|
||||
/* Default coalescing parameters */
|
||||
#define XGMAC_INIT_DMA_TX_USECS 100
|
||||
#define XGMAC_INIT_DMA_TX_FRAMES 16
|
||||
|
||||
#define XGMAC_MAX_DMA_RIWT 0xff
|
||||
#define XGMAC_INIT_DMA_RX_USECS 100
|
||||
#define XGMAC_INIT_DMA_RX_FRAMES 16
|
||||
|
||||
/* Flow control queue count */
|
||||
#define XGMAC_MAX_FLOW_CONTROL_QUEUES 8
|
||||
|
||||
|
||||
struct xgbe_prv_data;
|
||||
|
||||
struct xgbe_packet_data {
|
||||
unsigned int attributes;
|
||||
|
||||
unsigned int errors;
|
||||
|
||||
unsigned int rdesc_count;
|
||||
unsigned int length;
|
||||
|
||||
unsigned int header_len;
|
||||
unsigned int tcp_header_len;
|
||||
unsigned int tcp_payload_len;
|
||||
unsigned short mss;
|
||||
|
||||
unsigned short vlan_ctag;
|
||||
};
|
||||
|
||||
/* Common Rx and Tx descriptor mapping */
|
||||
struct xgbe_ring_desc {
|
||||
unsigned int desc0;
|
||||
unsigned int desc1;
|
||||
unsigned int desc2;
|
||||
unsigned int desc3;
|
||||
};
|
||||
|
||||
/* Structure used to hold information related to the descriptor
|
||||
* and the packet associated with the descriptor (always use
|
||||
* use the GET_DESC_DATA macro to access this data from the ring)
|
||||
*/
|
||||
struct xgbe_ring_data {
|
||||
struct xgbe_ring_desc *rdesc; /* Virtual address of descriptor */
|
||||
dma_addr_t rdesc_dma; /* DMA address of descriptor */
|
||||
|
||||
struct sk_buff *skb; /* Virtual address of SKB */
|
||||
dma_addr_t skb_dma; /* DMA address of SKB data */
|
||||
unsigned int skb_dma_len; /* Length of SKB DMA area */
|
||||
unsigned int tso_header; /* TSO header indicator */
|
||||
|
||||
unsigned short len; /* Length of received Rx packet */
|
||||
|
||||
unsigned int interrupt; /* Interrupt indicator */
|
||||
|
||||
unsigned int mapped_as_page;
|
||||
};
|
||||
|
||||
struct xgbe_ring {
|
||||
/* Ring lock - used just for TX rings at the moment */
|
||||
spinlock_t lock;
|
||||
|
||||
/* Per packet related information */
|
||||
struct xgbe_packet_data packet_data;
|
||||
|
||||
/* Virtual/DMA addresses and count of allocated descriptor memory */
|
||||
struct xgbe_ring_desc *rdesc;
|
||||
dma_addr_t rdesc_dma;
|
||||
unsigned int rdesc_count;
|
||||
|
||||
/* Array of descriptor data corresponding the descriptor memory
|
||||
* (always use the GET_DESC_DATA macro to access this data)
|
||||
*/
|
||||
struct xgbe_ring_data *rdata;
|
||||
|
||||
/* Ring index values
|
||||
* cur - Tx: index of descriptor to be used for current transfer
|
||||
* Rx: index of descriptor to check for packet availability
|
||||
* dirty - Tx: index of descriptor to check for transfer complete
|
||||
* Rx: count of descriptors in which a packet has been received
|
||||
* (used with skb_realloc_index to refresh the ring)
|
||||
*/
|
||||
unsigned int cur;
|
||||
unsigned int dirty;
|
||||
|
||||
/* Coalesce frame count used for interrupt bit setting */
|
||||
unsigned int coalesce_count;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned int queue_stopped;
|
||||
unsigned short cur_mss;
|
||||
unsigned short cur_vlan_ctag;
|
||||
} tx;
|
||||
|
||||
struct {
|
||||
unsigned int realloc_index;
|
||||
unsigned int realloc_threshold;
|
||||
} rx;
|
||||
};
|
||||
} ____cacheline_aligned;
|
||||
|
||||
/* Structure used to describe the descriptor rings associated with
|
||||
* a DMA channel.
|
||||
*/
|
||||
struct xgbe_channel {
|
||||
char name[16];
|
||||
|
||||
/* Address of private data area for device */
|
||||
struct xgbe_prv_data *pdata;
|
||||
|
||||
/* Queue index and base address of queue's DMA registers */
|
||||
unsigned int queue_index;
|
||||
void __iomem *dma_regs;
|
||||
|
||||
unsigned int saved_ier;
|
||||
|
||||
unsigned int tx_timer_active;
|
||||
struct hrtimer tx_timer;
|
||||
|
||||
struct xgbe_ring *tx_ring;
|
||||
struct xgbe_ring *rx_ring;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
enum xgbe_int {
|
||||
XGMAC_INT_DMA_ISR_DC0IS,
|
||||
XGMAC_INT_DMA_CH_SR_TI,
|
||||
XGMAC_INT_DMA_CH_SR_TPS,
|
||||
XGMAC_INT_DMA_CH_SR_TBU,
|
||||
XGMAC_INT_DMA_CH_SR_RI,
|
||||
XGMAC_INT_DMA_CH_SR_RBU,
|
||||
XGMAC_INT_DMA_CH_SR_RPS,
|
||||
XGMAC_INT_DMA_CH_SR_FBE,
|
||||
XGMAC_INT_DMA_ALL,
|
||||
};
|
||||
|
||||
enum xgbe_int_state {
|
||||
XGMAC_INT_STATE_SAVE,
|
||||
XGMAC_INT_STATE_RESTORE,
|
||||
};
|
||||
|
||||
enum xgbe_mtl_fifo_size {
|
||||
XGMAC_MTL_FIFO_SIZE_256 = 0x00,
|
||||
XGMAC_MTL_FIFO_SIZE_512 = 0x01,
|
||||
XGMAC_MTL_FIFO_SIZE_1K = 0x03,
|
||||
XGMAC_MTL_FIFO_SIZE_2K = 0x07,
|
||||
XGMAC_MTL_FIFO_SIZE_4K = 0x0f,
|
||||
XGMAC_MTL_FIFO_SIZE_8K = 0x1f,
|
||||
XGMAC_MTL_FIFO_SIZE_16K = 0x3f,
|
||||
XGMAC_MTL_FIFO_SIZE_32K = 0x7f,
|
||||
XGMAC_MTL_FIFO_SIZE_64K = 0xff,
|
||||
XGMAC_MTL_FIFO_SIZE_128K = 0x1ff,
|
||||
XGMAC_MTL_FIFO_SIZE_256K = 0x3ff,
|
||||
};
|
||||
|
||||
struct xgbe_mmc_stats {
|
||||
/* Tx Stats */
|
||||
u64 txoctetcount_gb;
|
||||
u64 txframecount_gb;
|
||||
u64 txbroadcastframes_g;
|
||||
u64 txmulticastframes_g;
|
||||
u64 tx64octets_gb;
|
||||
u64 tx65to127octets_gb;
|
||||
u64 tx128to255octets_gb;
|
||||
u64 tx256to511octets_gb;
|
||||
u64 tx512to1023octets_gb;
|
||||
u64 tx1024tomaxoctets_gb;
|
||||
u64 txunicastframes_gb;
|
||||
u64 txmulticastframes_gb;
|
||||
u64 txbroadcastframes_gb;
|
||||
u64 txunderflowerror;
|
||||
u64 txoctetcount_g;
|
||||
u64 txframecount_g;
|
||||
u64 txpauseframes;
|
||||
u64 txvlanframes_g;
|
||||
|
||||
/* Rx Stats */
|
||||
u64 rxframecount_gb;
|
||||
u64 rxoctetcount_gb;
|
||||
u64 rxoctetcount_g;
|
||||
u64 rxbroadcastframes_g;
|
||||
u64 rxmulticastframes_g;
|
||||
u64 rxcrcerror;
|
||||
u64 rxrunterror;
|
||||
u64 rxjabbererror;
|
||||
u64 rxundersize_g;
|
||||
u64 rxoversize_g;
|
||||
u64 rx64octets_gb;
|
||||
u64 rx65to127octets_gb;
|
||||
u64 rx128to255octets_gb;
|
||||
u64 rx256to511octets_gb;
|
||||
u64 rx512to1023octets_gb;
|
||||
u64 rx1024tomaxoctets_gb;
|
||||
u64 rxunicastframes_g;
|
||||
u64 rxlengtherror;
|
||||
u64 rxoutofrangetype;
|
||||
u64 rxpauseframes;
|
||||
u64 rxfifooverflow;
|
||||
u64 rxvlanframes_gb;
|
||||
u64 rxwatchdogerror;
|
||||
};
|
||||
|
||||
struct xgbe_hw_if {
|
||||
int (*tx_complete)(struct xgbe_ring_desc *);
|
||||
|
||||
int (*set_promiscuous_mode)(struct xgbe_prv_data *, unsigned int);
|
||||
int (*set_all_multicast_mode)(struct xgbe_prv_data *, unsigned int);
|
||||
int (*set_addn_mac_addrs)(struct xgbe_prv_data *, unsigned int);
|
||||
int (*set_mac_address)(struct xgbe_prv_data *, u8 *addr);
|
||||
|
||||
int (*enable_rx_csum)(struct xgbe_prv_data *);
|
||||
int (*disable_rx_csum)(struct xgbe_prv_data *);
|
||||
|
||||
int (*enable_rx_vlan_stripping)(struct xgbe_prv_data *);
|
||||
int (*disable_rx_vlan_stripping)(struct xgbe_prv_data *);
|
||||
|
||||
int (*read_mmd_regs)(struct xgbe_prv_data *, int, int);
|
||||
void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int);
|
||||
int (*set_gmii_speed)(struct xgbe_prv_data *);
|
||||
int (*set_gmii_2500_speed)(struct xgbe_prv_data *);
|
||||
int (*set_xgmii_speed)(struct xgbe_prv_data *);
|
||||
|
||||
void (*enable_tx)(struct xgbe_prv_data *);
|
||||
void (*disable_tx)(struct xgbe_prv_data *);
|
||||
void (*enable_rx)(struct xgbe_prv_data *);
|
||||
void (*disable_rx)(struct xgbe_prv_data *);
|
||||
|
||||
void (*powerup_tx)(struct xgbe_prv_data *);
|
||||
void (*powerdown_tx)(struct xgbe_prv_data *);
|
||||
void (*powerup_rx)(struct xgbe_prv_data *);
|
||||
void (*powerdown_rx)(struct xgbe_prv_data *);
|
||||
|
||||
int (*init)(struct xgbe_prv_data *);
|
||||
int (*exit)(struct xgbe_prv_data *);
|
||||
|
||||
int (*enable_int)(struct xgbe_channel *, enum xgbe_int);
|
||||
int (*disable_int)(struct xgbe_channel *, enum xgbe_int);
|
||||
void (*pre_xmit)(struct xgbe_channel *);
|
||||
int (*dev_read)(struct xgbe_channel *);
|
||||
void (*tx_desc_init)(struct xgbe_channel *);
|
||||
void (*rx_desc_init)(struct xgbe_channel *);
|
||||
void (*rx_desc_reset)(struct xgbe_ring_data *);
|
||||
void (*tx_desc_reset)(struct xgbe_ring_data *);
|
||||
int (*is_last_desc)(struct xgbe_ring_desc *);
|
||||
int (*is_context_desc)(struct xgbe_ring_desc *);
|
||||
|
||||
/* For FLOW ctrl */
|
||||
int (*config_tx_flow_control)(struct xgbe_prv_data *);
|
||||
int (*config_rx_flow_control)(struct xgbe_prv_data *);
|
||||
|
||||
/* For RX coalescing */
|
||||
int (*config_rx_coalesce)(struct xgbe_prv_data *);
|
||||
int (*config_tx_coalesce)(struct xgbe_prv_data *);
|
||||
unsigned int (*usec_to_riwt)(struct xgbe_prv_data *, unsigned int);
|
||||
unsigned int (*riwt_to_usec)(struct xgbe_prv_data *, unsigned int);
|
||||
|
||||
/* For RX and TX threshold config */
|
||||
int (*config_rx_threshold)(struct xgbe_prv_data *, unsigned int);
|
||||
int (*config_tx_threshold)(struct xgbe_prv_data *, unsigned int);
|
||||
|
||||
/* For RX and TX Store and Forward Mode config */
|
||||
int (*config_rsf_mode)(struct xgbe_prv_data *, unsigned int);
|
||||
int (*config_tsf_mode)(struct xgbe_prv_data *, unsigned int);
|
||||
|
||||
/* For TX DMA Operate on Second Frame config */
|
||||
int (*config_osp_mode)(struct xgbe_prv_data *);
|
||||
|
||||
/* For RX and TX PBL config */
|
||||
int (*config_rx_pbl_val)(struct xgbe_prv_data *);
|
||||
int (*get_rx_pbl_val)(struct xgbe_prv_data *);
|
||||
int (*config_tx_pbl_val)(struct xgbe_prv_data *);
|
||||
int (*get_tx_pbl_val)(struct xgbe_prv_data *);
|
||||
int (*config_pblx8)(struct xgbe_prv_data *);
|
||||
|
||||
/* For MMC statistics */
|
||||
void (*rx_mmc_int)(struct xgbe_prv_data *);
|
||||
void (*tx_mmc_int)(struct xgbe_prv_data *);
|
||||
void (*read_mmc_stats)(struct xgbe_prv_data *);
|
||||
};
|
||||
|
||||
struct xgbe_desc_if {
|
||||
int (*alloc_ring_resources)(struct xgbe_prv_data *);
|
||||
void (*free_ring_resources)(struct xgbe_prv_data *);
|
||||
int (*map_tx_skb)(struct xgbe_channel *, struct sk_buff *);
|
||||
void (*realloc_skb)(struct xgbe_channel *);
|
||||
void (*unmap_skb)(struct xgbe_prv_data *, struct xgbe_ring_data *);
|
||||
void (*wrapper_tx_desc_init)(struct xgbe_prv_data *);
|
||||
void (*wrapper_rx_desc_init)(struct xgbe_prv_data *);
|
||||
};
|
||||
|
||||
/* This structure contains flags that indicate what hardware features
|
||||
* or configurations are present in the device.
|
||||
*/
|
||||
struct xgbe_hw_features {
|
||||
/* HW Feature Register0 */
|
||||
unsigned int gmii; /* 1000 Mbps support */
|
||||
unsigned int vlhash; /* VLAN Hash Filter */
|
||||
unsigned int sma; /* SMA(MDIO) Interface */
|
||||
unsigned int rwk; /* PMT remote wake-up packet */
|
||||
unsigned int mgk; /* PMT magic packet */
|
||||
unsigned int mmc; /* RMON module */
|
||||
unsigned int aoe; /* ARP Offload */
|
||||
unsigned int ts; /* IEEE 1588-2008 Adavanced Timestamp */
|
||||
unsigned int eee; /* Energy Efficient Ethernet */
|
||||
unsigned int tx_coe; /* Tx Checksum Offload */
|
||||
unsigned int rx_coe; /* Rx Checksum Offload */
|
||||
unsigned int addn_mac; /* Additional MAC Addresses */
|
||||
unsigned int ts_src; /* Timestamp Source */
|
||||
unsigned int sa_vlan_ins; /* Source Address or VLAN Insertion */
|
||||
|
||||
/* HW Feature Register1 */
|
||||
unsigned int rx_fifo_size; /* MTL Receive FIFO Size */
|
||||
unsigned int tx_fifo_size; /* MTL Transmit FIFO Size */
|
||||
unsigned int adv_ts_hi; /* Advance Timestamping High Word */
|
||||
unsigned int dcb; /* DCB Feature */
|
||||
unsigned int sph; /* Split Header Feature */
|
||||
unsigned int tso; /* TCP Segmentation Offload */
|
||||
unsigned int dma_debug; /* DMA Debug Registers */
|
||||
unsigned int rss; /* Receive Side Scaling */
|
||||
unsigned int hash_table_size; /* Hash Table Size */
|
||||
unsigned int l3l4_filter_num; /* Number of L3-L4 Filters */
|
||||
|
||||
/* HW Feature Register2 */
|
||||
unsigned int rx_q_cnt; /* Number of MTL Receive Queues */
|
||||
unsigned int tx_q_cnt; /* Number of MTL Transmit Queues */
|
||||
unsigned int rx_ch_cnt; /* Number of DMA Receive Channels */
|
||||
unsigned int tx_ch_cnt; /* Number of DMA Transmit Channels */
|
||||
unsigned int pps_out_num; /* Number of PPS outputs */
|
||||
unsigned int aux_snap_num; /* Number of Aux snapshot inputs */
|
||||
};
|
||||
|
||||
struct xgbe_prv_data {
|
||||
struct net_device *netdev;
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
|
||||
/* XGMAC/XPCS related mmio registers */
|
||||
void __iomem *xgmac_regs; /* XGMAC CSRs */
|
||||
void __iomem *xpcs_regs; /* XPCS MMD registers */
|
||||
|
||||
/* Overall device lock */
|
||||
spinlock_t lock;
|
||||
|
||||
/* XPCS indirect addressing mutex */
|
||||
struct mutex xpcs_mutex;
|
||||
|
||||
int irq_number;
|
||||
|
||||
struct xgbe_hw_if hw_if;
|
||||
struct xgbe_desc_if desc_if;
|
||||
|
||||
/* Rings for Tx/Rx on a DMA channel */
|
||||
struct xgbe_channel *channel;
|
||||
unsigned int channel_count;
|
||||
unsigned int tx_ring_count;
|
||||
unsigned int tx_desc_count;
|
||||
unsigned int rx_ring_count;
|
||||
unsigned int rx_desc_count;
|
||||
|
||||
/* Tx/Rx common settings */
|
||||
unsigned int pblx8;
|
||||
|
||||
/* Tx settings */
|
||||
unsigned int tx_sf_mode;
|
||||
unsigned int tx_threshold;
|
||||
unsigned int tx_pbl;
|
||||
unsigned int tx_osp_mode;
|
||||
|
||||
/* Rx settings */
|
||||
unsigned int rx_sf_mode;
|
||||
unsigned int rx_threshold;
|
||||
unsigned int rx_pbl;
|
||||
|
||||
/* Tx coalescing settings */
|
||||
unsigned int tx_usecs;
|
||||
unsigned int tx_frames;
|
||||
|
||||
/* Rx coalescing settings */
|
||||
unsigned int rx_riwt;
|
||||
unsigned int rx_frames;
|
||||
|
||||
/* Current MTU */
|
||||
unsigned int rx_buf_size;
|
||||
|
||||
/* Flow control settings */
|
||||
unsigned int pause_autoneg;
|
||||
unsigned int tx_pause;
|
||||
unsigned int rx_pause;
|
||||
|
||||
/* MDIO settings */
|
||||
struct module *phy_module;
|
||||
char *mii_bus_id;
|
||||
struct mii_bus *mii;
|
||||
int mdio_mmd;
|
||||
struct phy_device *phydev;
|
||||
int default_autoneg;
|
||||
int default_speed;
|
||||
|
||||
/* Current PHY settings */
|
||||
phy_interface_t phy_mode;
|
||||
int phy_link;
|
||||
int phy_speed;
|
||||
unsigned int phy_tx_pause;
|
||||
unsigned int phy_rx_pause;
|
||||
|
||||
/* Netdev related settings */
|
||||
netdev_features_t netdev_features;
|
||||
struct napi_struct napi;
|
||||
struct xgbe_mmc_stats mmc_stats;
|
||||
|
||||
/* System clock value used for Rx watchdog */
|
||||
struct clk *sysclock;
|
||||
|
||||
/* Hardware features of the device */
|
||||
struct xgbe_hw_features hw_feat;
|
||||
|
||||
/* Device restart work structure */
|
||||
struct work_struct restart_work;
|
||||
|
||||
/* Keeps track of power mode */
|
||||
unsigned int power_down;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *xgbe_debugfs;
|
||||
|
||||
unsigned int debugfs_xgmac_reg;
|
||||
|
||||
unsigned int debugfs_xpcs_mmd;
|
||||
unsigned int debugfs_xpcs_reg;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Function prototypes*/
|
||||
|
||||
void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *);
|
||||
void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *);
|
||||
struct net_device_ops *xgbe_get_netdev_ops(void);
|
||||
struct ethtool_ops *xgbe_get_ethtool_ops(void);
|
||||
|
||||
int xgbe_mdio_register(struct xgbe_prv_data *);
|
||||
void xgbe_mdio_unregister(struct xgbe_prv_data *);
|
||||
void xgbe_dump_phy_registers(struct xgbe_prv_data *);
|
||||
void xgbe_dump_tx_desc(struct xgbe_ring *, unsigned int, unsigned int,
|
||||
unsigned int);
|
||||
void xgbe_dump_rx_desc(struct xgbe_ring *, struct xgbe_ring_desc *,
|
||||
unsigned int);
|
||||
void xgbe_print_pkt(struct net_device *, struct sk_buff *, bool);
|
||||
void xgbe_get_all_hw_features(struct xgbe_prv_data *);
|
||||
int xgbe_powerup(struct net_device *, unsigned int);
|
||||
int xgbe_powerdown(struct net_device *, unsigned int);
|
||||
void xgbe_init_rx_coalesce(struct xgbe_prv_data *);
|
||||
void xgbe_init_tx_coalesce(struct xgbe_prv_data *);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void xgbe_debugfs_init(struct xgbe_prv_data *);
|
||||
void xgbe_debugfs_exit(struct xgbe_prv_data *);
|
||||
#else
|
||||
static inline void xgbe_debugfs_init(struct xgbe_prv_data *pdata) {}
|
||||
static inline void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) {}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
/* NOTE: Uncomment for TX and RX DESCRIPTOR DUMP in KERNEL LOG */
|
||||
#if 0
|
||||
#define XGMAC_ENABLE_TX_DESC_DUMP
|
||||
#define XGMAC_ENABLE_RX_DESC_DUMP
|
||||
#endif
|
||||
|
||||
/* NOTE: Uncomment for TX and RX PACKET DUMP in KERNEL LOG */
|
||||
#if 0
|
||||
#define XGMAC_ENABLE_TX_PKT_DUMP
|
||||
#define XGMAC_ENABLE_RX_PKT_DUMP
|
||||
#endif
|
||||
|
||||
/* NOTE: Uncomment for function trace log messages in KERNEL LOG */
|
||||
#if 0
|
||||
#define YDEBUG
|
||||
#define YDEBUG_MDIO
|
||||
#endif
|
||||
|
||||
/* For debug prints */
|
||||
#ifdef YDEBUG
|
||||
#define DBGPR(x...) pr_alert(x)
|
||||
#define DBGPHY_REGS(x...) xgbe_dump_phy_registers(x)
|
||||
#else
|
||||
#define DBGPR(x...) do { } while (0)
|
||||
#define DBGPHY_REGS(x...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef YDEBUG_MDIO
|
||||
#define DBGPR_MDIO(x...) pr_alert(x)
|
||||
#else
|
||||
#define DBGPR_MDIO(x...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
* Vineet Gupta
|
||||
*/
|
||||
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -362,6 +363,15 @@ static irqreturn_t arc_emac_intr(int irq, void *dev_instance)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
static void arc_emac_poll_controller(struct net_device *dev)
|
||||
{
|
||||
disable_irq(dev->irq);
|
||||
arc_emac_intr(dev->irq, dev);
|
||||
enable_irq(dev->irq);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* arc_emac_open - Open the network device.
|
||||
* @ndev: Pointer to the network device.
|
||||
|
|
@ -450,6 +460,41 @@ static int arc_emac_open(struct net_device *ndev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* arc_emac_set_rx_mode - Change the receive filtering mode.
|
||||
* @ndev: Pointer to the network device.
|
||||
*
|
||||
* This function enables/disables promiscuous or all-multicast mode
|
||||
* and updates the multicast filtering list of the network device.
|
||||
*/
|
||||
static void arc_emac_set_rx_mode(struct net_device *ndev)
|
||||
{
|
||||
struct arc_emac_priv *priv = netdev_priv(ndev);
|
||||
|
||||
if (ndev->flags & IFF_PROMISC) {
|
||||
arc_reg_or(priv, R_CTRL, PROM_MASK);
|
||||
} else {
|
||||
arc_reg_clr(priv, R_CTRL, PROM_MASK);
|
||||
|
||||
if (ndev->flags & IFF_ALLMULTI) {
|
||||
arc_reg_set(priv, R_LAFL, ~0);
|
||||
arc_reg_set(priv, R_LAFH, ~0);
|
||||
} else {
|
||||
struct netdev_hw_addr *ha;
|
||||
unsigned int filter[2] = { 0, 0 };
|
||||
int bit;
|
||||
|
||||
netdev_for_each_mc_addr(ha, ndev) {
|
||||
bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26;
|
||||
filter[bit >> 5] |= 1 << (bit & 31);
|
||||
}
|
||||
|
||||
arc_reg_set(priv, R_LAFL, filter[0]);
|
||||
arc_reg_set(priv, R_LAFH, filter[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* arc_emac_stop - Close the network device.
|
||||
* @ndev: Pointer to the network device.
|
||||
|
|
@ -620,6 +665,10 @@ static const struct net_device_ops arc_emac_netdev_ops = {
|
|||
.ndo_start_xmit = arc_emac_tx,
|
||||
.ndo_set_mac_address = arc_emac_set_address,
|
||||
.ndo_get_stats = arc_emac_stats,
|
||||
.ndo_set_rx_mode = arc_emac_set_rx_mode,
|
||||
#ifdef CONFIG_NET_POLL_CONTROLLER
|
||||
.ndo_poll_controller = arc_emac_poll_controller,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int arc_emac_probe(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -1302,7 +1302,7 @@ static int alx_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
}
|
||||
|
||||
netdev->netdev_ops = &alx_netdev_ops;
|
||||
SET_ETHTOOL_OPS(netdev, &alx_ethtool_ops);
|
||||
netdev->ethtool_ops = &alx_ethtool_ops;
|
||||
netdev->irq = pdev->irq;
|
||||
netdev->watchdog_timeo = ALX_WATCHDOG_TIME;
|
||||
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ static int atl1c_get_settings(struct net_device *netdev,
|
|||
else
|
||||
ecmd->duplex = DUPLEX_HALF;
|
||||
} else {
|
||||
ethtool_cmd_speed_set(ecmd, -1);
|
||||
ecmd->duplex = -1;
|
||||
ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
|
||||
ecmd->duplex = DUPLEX_UNKNOWN;
|
||||
}
|
||||
|
||||
ecmd->autoneg = AUTONEG_ENABLE;
|
||||
|
|
@ -305,5 +305,5 @@ static const struct ethtool_ops atl1c_ethtool_ops = {
|
|||
|
||||
void atl1c_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
SET_ETHTOOL_OPS(netdev, &atl1c_ethtool_ops);
|
||||
netdev->ethtool_ops = &atl1c_ethtool_ops;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ static int atl1e_get_settings(struct net_device *netdev,
|
|||
else
|
||||
ecmd->duplex = DUPLEX_HALF;
|
||||
} else {
|
||||
ethtool_cmd_speed_set(ecmd, -1);
|
||||
ecmd->duplex = -1;
|
||||
ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
|
||||
ecmd->duplex = DUPLEX_UNKNOWN;
|
||||
}
|
||||
|
||||
ecmd->autoneg = AUTONEG_ENABLE;
|
||||
|
|
@ -388,5 +388,5 @@ static const struct ethtool_ops atl1e_ethtool_ops = {
|
|||
|
||||
void atl1e_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
SET_ETHTOOL_OPS(netdev, &atl1e_ethtool_ops);
|
||||
netdev->ethtool_ops = &atl1e_ethtool_ops;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3258,8 +3258,8 @@ static int atl1_get_settings(struct net_device *netdev,
|
|||
else
|
||||
ecmd->duplex = DUPLEX_HALF;
|
||||
} else {
|
||||
ethtool_cmd_speed_set(ecmd, -1);
|
||||
ecmd->duplex = -1;
|
||||
ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
|
||||
ecmd->duplex = DUPLEX_UNKNOWN;
|
||||
}
|
||||
if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
|
||||
hw->media_type == MEDIA_TYPE_1000M_FULL)
|
||||
|
|
|
|||
|
|
@ -1396,7 +1396,7 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
atl2_setup_pcicmd(pdev);
|
||||
|
||||
netdev->netdev_ops = &atl2_netdev_ops;
|
||||
SET_ETHTOOL_OPS(netdev, &atl2_ethtool_ops);
|
||||
netdev->ethtool_ops = &atl2_ethtool_ops;
|
||||
netdev->watchdog_timeo = 5 * HZ;
|
||||
strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
|
||||
|
||||
|
|
@ -1769,8 +1769,8 @@ static int atl2_get_settings(struct net_device *netdev,
|
|||
else
|
||||
ecmd->duplex = DUPLEX_HALF;
|
||||
} else {
|
||||
ethtool_cmd_speed_set(ecmd, -1);
|
||||
ecmd->duplex = -1;
|
||||
ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
|
||||
ecmd->duplex = DUPLEX_UNKNOWN;
|
||||
}
|
||||
|
||||
ecmd->autoneg = AUTONEG_ENABLE;
|
||||
|
|
|
|||
|
|
@ -150,4 +150,15 @@ config BGMAC
|
|||
In case of using this driver on BCM4706 it's also requires to enable
|
||||
BCMA_DRIVER_GMAC_CMN to make it work.
|
||||
|
||||
config SYSTEMPORT
|
||||
tristate "Broadcom SYSTEMPORT internal MAC support"
|
||||
depends on OF
|
||||
select MII
|
||||
select PHYLIB
|
||||
select FIXED_PHY if SYSTEMPORT=y
|
||||
help
|
||||
This driver supports the built-in Ethernet MACs found in the
|
||||
Broadcom BCM7xxx Set Top Box family chipset using an internal
|
||||
Ethernet switch.
|
||||
|
||||
endif # NET_VENDOR_BROADCOM
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@ obj-$(CONFIG_BNX2X) += bnx2x/
|
|||
obj-$(CONFIG_SB1250_MAC) += sb1250-mac.o
|
||||
obj-$(CONFIG_TIGON3) += tg3.o
|
||||
obj-$(CONFIG_BGMAC) += bgmac.o
|
||||
obj-$(CONFIG_SYSTEMPORT) += bcmsysport.o
|
||||
|
|
|
|||
|
|
@ -2380,7 +2380,7 @@ static int b44_init_one(struct ssb_device *sdev,
|
|||
netif_napi_add(dev, &bp->napi, b44_poll, 64);
|
||||
dev->watchdog_timeo = B44_TX_TIMEOUT;
|
||||
dev->irq = sdev->irq;
|
||||
SET_ETHTOOL_OPS(dev, &b44_ethtool_ops);
|
||||
dev->ethtool_ops = &b44_ethtool_ops;
|
||||
|
||||
err = ssb_bus_powerup(sdev->bus, 0);
|
||||
if (err) {
|
||||
|
|
|
|||
|
|
@ -1315,8 +1315,7 @@ static const struct bcm_enet_stats bcm_enet_gstrings_stats[] = {
|
|||
|
||||
};
|
||||
|
||||
#define BCM_ENET_STATS_LEN \
|
||||
(sizeof(bcm_enet_gstrings_stats) / sizeof(struct bcm_enet_stats))
|
||||
#define BCM_ENET_STATS_LEN ARRAY_SIZE(bcm_enet_gstrings_stats)
|
||||
|
||||
static const u32 unused_mib_regs[] = {
|
||||
ETH_MIB_TX_ALL_OCTETS,
|
||||
|
|
@ -1898,7 +1897,7 @@ static int bcm_enet_probe(struct platform_device *pdev)
|
|||
dev->netdev_ops = &bcm_enet_ops;
|
||||
netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16);
|
||||
|
||||
SET_ETHTOOL_OPS(dev, &bcm_enet_ethtool_ops);
|
||||
dev->ethtool_ops = &bcm_enet_ethtool_ops;
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
|
||||
ret = register_netdev(dev);
|
||||
|
|
@ -2784,7 +2783,7 @@ static int bcm_enetsw_probe(struct platform_device *pdev)
|
|||
/* register netdevice */
|
||||
dev->netdev_ops = &bcm_enetsw_ops;
|
||||
netif_napi_add(dev, &priv->napi, bcm_enet_poll, 16);
|
||||
SET_ETHTOOL_OPS(dev, &bcm_enetsw_ethtool_ops);
|
||||
dev->ethtool_ops = &bcm_enetsw_ethtool_ops;
|
||||
SET_NETDEV_DEV(dev, &pdev->dev);
|
||||
|
||||
spin_lock_init(&priv->enetsw_mdio_lock);
|
||||
|
|
|
|||
1654
drivers/net/ethernet/broadcom/bcmsysport.c
Normal file
1654
drivers/net/ethernet/broadcom/bcmsysport.c
Normal file
File diff suppressed because it is too large
Load diff
678
drivers/net/ethernet/broadcom/bcmsysport.h
Normal file
678
drivers/net/ethernet/broadcom/bcmsysport.h
Normal file
|
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
* Broadcom BCM7xxx System Port Ethernet MAC driver
|
||||
*
|
||||
* Copyright (C) 2014 Broadcom Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __BCM_SYSPORT_H
|
||||
#define __BCM_SYSPORT_H
|
||||
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
/* Receive/transmit descriptor format */
|
||||
#define DESC_ADDR_HI_STATUS_LEN 0x00
|
||||
#define DESC_ADDR_HI_SHIFT 0
|
||||
#define DESC_ADDR_HI_MASK 0xff
|
||||
#define DESC_STATUS_SHIFT 8
|
||||
#define DESC_STATUS_MASK 0x3ff
|
||||
#define DESC_LEN_SHIFT 18
|
||||
#define DESC_LEN_MASK 0x7fff
|
||||
#define DESC_ADDR_LO 0x04
|
||||
|
||||
/* HW supports 40-bit addressing hence the */
|
||||
#define DESC_SIZE (WORDS_PER_DESC * sizeof(u32))
|
||||
|
||||
/* Default RX buffer allocation size */
|
||||
#define RX_BUF_LENGTH 2048
|
||||
|
||||
/* Body(1500) + EH_SIZE(14) + VLANTAG(4) + BRCMTAG(4) + FCS(4) = 1526.
|
||||
* 1536 is multiple of 256 bytes
|
||||
*/
|
||||
#define ENET_BRCM_TAG_LEN 4
|
||||
#define ENET_PAD 10
|
||||
#define UMAC_MAX_MTU_SIZE (ETH_DATA_LEN + ETH_HLEN + VLAN_HLEN + \
|
||||
ENET_BRCM_TAG_LEN + ETH_FCS_LEN + ENET_PAD)
|
||||
|
||||
/* Transmit status block */
|
||||
struct bcm_tsb {
|
||||
u32 pcp_dei_vid;
|
||||
#define PCP_DEI_MASK 0xf
|
||||
#define VID_SHIFT 4
|
||||
#define VID_MASK 0xfff
|
||||
u32 l4_ptr_dest_map;
|
||||
#define L4_CSUM_PTR_MASK 0x1ff
|
||||
#define L4_PTR_SHIFT 9
|
||||
#define L4_PTR_MASK 0x1ff
|
||||
#define L4_UDP (1 << 18)
|
||||
#define L4_LENGTH_VALID (1 << 19)
|
||||
#define DEST_MAP_SHIFT 20
|
||||
#define DEST_MAP_MASK 0x1ff
|
||||
};
|
||||
|
||||
/* Receive status block uses the same
|
||||
* definitions as the DMA descriptor
|
||||
*/
|
||||
struct bcm_rsb {
|
||||
u32 rx_status_len;
|
||||
u32 brcm_egress_tag;
|
||||
};
|
||||
|
||||
/* Common Receive/Transmit status bits */
|
||||
#define DESC_L4_CSUM (1 << 7)
|
||||
#define DESC_SOP (1 << 8)
|
||||
#define DESC_EOP (1 << 9)
|
||||
|
||||
/* Receive Status bits */
|
||||
#define RX_STATUS_UCAST 0
|
||||
#define RX_STATUS_BCAST 0x04
|
||||
#define RX_STATUS_MCAST 0x08
|
||||
#define RX_STATUS_L2_MCAST 0x0c
|
||||
#define RX_STATUS_ERR (1 << 4)
|
||||
#define RX_STATUS_OVFLOW (1 << 5)
|
||||
#define RX_STATUS_PARSE_FAIL (1 << 6)
|
||||
|
||||
/* Transmit Status bits */
|
||||
#define TX_STATUS_VLAN_NO_ACT 0x00
|
||||
#define TX_STATUS_VLAN_PCP_TSB 0x01
|
||||
#define TX_STATUS_VLAN_QUEUE 0x02
|
||||
#define TX_STATUS_VLAN_VID_TSB 0x03
|
||||
#define TX_STATUS_OWR_CRC (1 << 2)
|
||||
#define TX_STATUS_APP_CRC (1 << 3)
|
||||
#define TX_STATUS_BRCM_TAG_NO_ACT 0
|
||||
#define TX_STATUS_BRCM_TAG_ZERO 0x10
|
||||
#define TX_STATUS_BRCM_TAG_ONE_QUEUE 0x20
|
||||
#define TX_STATUS_BRCM_TAG_ONE_TSB 0x30
|
||||
#define TX_STATUS_SKIP_BYTES (1 << 6)
|
||||
|
||||
/* Specific register definitions */
|
||||
#define SYS_PORT_TOPCTRL_OFFSET 0
|
||||
#define REV_CNTL 0x00
|
||||
#define REV_MASK 0xffff
|
||||
|
||||
#define RX_FLUSH_CNTL 0x04
|
||||
#define RX_FLUSH (1 << 0)
|
||||
|
||||
#define TX_FLUSH_CNTL 0x08
|
||||
#define TX_FLUSH (1 << 0)
|
||||
|
||||
#define MISC_CNTL 0x0c
|
||||
#define SYS_CLK_SEL (1 << 0)
|
||||
#define TDMA_EOP_SEL (1 << 1)
|
||||
|
||||
/* Level-2 Interrupt controller offsets and defines */
|
||||
#define SYS_PORT_INTRL2_0_OFFSET 0x200
|
||||
#define SYS_PORT_INTRL2_1_OFFSET 0x240
|
||||
#define INTRL2_CPU_STATUS 0x00
|
||||
#define INTRL2_CPU_SET 0x04
|
||||
#define INTRL2_CPU_CLEAR 0x08
|
||||
#define INTRL2_CPU_MASK_STATUS 0x0c
|
||||
#define INTRL2_CPU_MASK_SET 0x10
|
||||
#define INTRL2_CPU_MASK_CLEAR 0x14
|
||||
|
||||
/* Level-2 instance 0 interrupt bits */
|
||||
#define INTRL2_0_GISB_ERR (1 << 0)
|
||||
#define INTRL2_0_RBUF_OVFLOW (1 << 1)
|
||||
#define INTRL2_0_TBUF_UNDFLOW (1 << 2)
|
||||
#define INTRL2_0_MPD (1 << 3)
|
||||
#define INTRL2_0_BRCM_MATCH_TAG (1 << 4)
|
||||
#define INTRL2_0_RDMA_MBDONE (1 << 5)
|
||||
#define INTRL2_0_OVER_MAX_THRESH (1 << 6)
|
||||
#define INTRL2_0_BELOW_HYST_THRESH (1 << 7)
|
||||
#define INTRL2_0_FREE_LIST_EMPTY (1 << 8)
|
||||
#define INTRL2_0_TX_RING_FULL (1 << 9)
|
||||
#define INTRL2_0_DESC_ALLOC_ERR (1 << 10)
|
||||
#define INTRL2_0_UNEXP_PKTSIZE_ACK (1 << 11)
|
||||
|
||||
/* RXCHK offset and defines */
|
||||
#define SYS_PORT_RXCHK_OFFSET 0x300
|
||||
|
||||
#define RXCHK_CONTROL 0x00
|
||||
#define RXCHK_EN (1 << 0)
|
||||
#define RXCHK_SKIP_FCS (1 << 1)
|
||||
#define RXCHK_BAD_CSUM_DIS (1 << 2)
|
||||
#define RXCHK_BRCM_TAG_EN (1 << 3)
|
||||
#define RXCHK_BRCM_TAG_MATCH_SHIFT 4
|
||||
#define RXCHK_BRCM_TAG_MATCH_MASK 0xff
|
||||
#define RXCHK_PARSE_TNL (1 << 12)
|
||||
#define RXCHK_VIOL_EN (1 << 13)
|
||||
#define RXCHK_VIOL_DIS (1 << 14)
|
||||
#define RXCHK_INCOM_PKT (1 << 15)
|
||||
#define RXCHK_V6_DUPEXT_EN (1 << 16)
|
||||
#define RXCHK_V6_DUPEXT_DIS (1 << 17)
|
||||
#define RXCHK_ETHERTYPE_DIS (1 << 18)
|
||||
#define RXCHK_L2_HDR_DIS (1 << 19)
|
||||
#define RXCHK_L3_HDR_DIS (1 << 20)
|
||||
#define RXCHK_MAC_RX_ERR_DIS (1 << 21)
|
||||
#define RXCHK_PARSE_AUTH (1 << 22)
|
||||
|
||||
#define RXCHK_BRCM_TAG0 0x04
|
||||
#define RXCHK_BRCM_TAG(i) ((i) * RXCHK_BRCM_TAG0)
|
||||
#define RXCHK_BRCM_TAG0_MASK 0x24
|
||||
#define RXCHK_BRCM_TAG_MASK(i) ((i) * RXCHK_BRCM_TAG0_MASK)
|
||||
#define RXCHK_BRCM_TAG_MATCH_STATUS 0x44
|
||||
#define RXCHK_ETHERTYPE 0x48
|
||||
#define RXCHK_BAD_CSUM_CNTR 0x4C
|
||||
#define RXCHK_OTHER_DISC_CNTR 0x50
|
||||
|
||||
/* TXCHCK offsets and defines */
|
||||
#define SYS_PORT_TXCHK_OFFSET 0x380
|
||||
#define TXCHK_PKT_RDY_THRESH 0x00
|
||||
|
||||
/* Receive buffer offset and defines */
|
||||
#define SYS_PORT_RBUF_OFFSET 0x400
|
||||
|
||||
#define RBUF_CONTROL 0x00
|
||||
#define RBUF_RSB_EN (1 << 0)
|
||||
#define RBUF_4B_ALGN (1 << 1)
|
||||
#define RBUF_BRCM_TAG_STRIP (1 << 2)
|
||||
#define RBUF_BAD_PKT_DISC (1 << 3)
|
||||
#define RBUF_RESUME_THRESH_SHIFT 4
|
||||
#define RBUF_RESUME_THRESH_MASK 0xff
|
||||
#define RBUF_OK_TO_SEND_SHIFT 12
|
||||
#define RBUF_OK_TO_SEND_MASK 0xff
|
||||
#define RBUF_CRC_REPLACE (1 << 20)
|
||||
#define RBUF_OK_TO_SEND_MODE (1 << 21)
|
||||
#define RBUF_RSB_SWAP (1 << 22)
|
||||
#define RBUF_ACPI_EN (1 << 23)
|
||||
|
||||
#define RBUF_PKT_RDY_THRESH 0x04
|
||||
|
||||
#define RBUF_STATUS 0x08
|
||||
#define RBUF_WOL_MODE (1 << 0)
|
||||
#define RBUF_MPD (1 << 1)
|
||||
#define RBUF_ACPI (1 << 2)
|
||||
|
||||
#define RBUF_OVFL_DISC_CNTR 0x0c
|
||||
#define RBUF_ERR_PKT_CNTR 0x10
|
||||
|
||||
/* Transmit buffer offset and defines */
|
||||
#define SYS_PORT_TBUF_OFFSET 0x600
|
||||
|
||||
#define TBUF_CONTROL 0x00
|
||||
#define TBUF_BP_EN (1 << 0)
|
||||
#define TBUF_MAX_PKT_THRESH_SHIFT 1
|
||||
#define TBUF_MAX_PKT_THRESH_MASK 0x1f
|
||||
#define TBUF_FULL_THRESH_SHIFT 8
|
||||
#define TBUF_FULL_THRESH_MASK 0x1f
|
||||
|
||||
/* UniMAC offset and defines */
|
||||
#define SYS_PORT_UMAC_OFFSET 0x800
|
||||
|
||||
#define UMAC_CMD 0x008
|
||||
#define CMD_TX_EN (1 << 0)
|
||||
#define CMD_RX_EN (1 << 1)
|
||||
#define CMD_SPEED_SHIFT 2
|
||||
#define CMD_SPEED_10 0
|
||||
#define CMD_SPEED_100 1
|
||||
#define CMD_SPEED_1000 2
|
||||
#define CMD_SPEED_2500 3
|
||||
#define CMD_SPEED_MASK 3
|
||||
#define CMD_PROMISC (1 << 4)
|
||||
#define CMD_PAD_EN (1 << 5)
|
||||
#define CMD_CRC_FWD (1 << 6)
|
||||
#define CMD_PAUSE_FWD (1 << 7)
|
||||
#define CMD_RX_PAUSE_IGNORE (1 << 8)
|
||||
#define CMD_TX_ADDR_INS (1 << 9)
|
||||
#define CMD_HD_EN (1 << 10)
|
||||
#define CMD_SW_RESET (1 << 13)
|
||||
#define CMD_LCL_LOOP_EN (1 << 15)
|
||||
#define CMD_AUTO_CONFIG (1 << 22)
|
||||
#define CMD_CNTL_FRM_EN (1 << 23)
|
||||
#define CMD_NO_LEN_CHK (1 << 24)
|
||||
#define CMD_RMT_LOOP_EN (1 << 25)
|
||||
#define CMD_PRBL_EN (1 << 27)
|
||||
#define CMD_TX_PAUSE_IGNORE (1 << 28)
|
||||
#define CMD_TX_RX_EN (1 << 29)
|
||||
#define CMD_RUNT_FILTER_DIS (1 << 30)
|
||||
|
||||
#define UMAC_MAC0 0x00c
|
||||
#define UMAC_MAC1 0x010
|
||||
#define UMAC_MAX_FRAME_LEN 0x014
|
||||
|
||||
#define UMAC_TX_FLUSH 0x334
|
||||
|
||||
#define UMAC_MIB_START 0x400
|
||||
|
||||
/* There is a 0xC gap between the end of RX and beginning of TX stats and then
|
||||
* between the end of TX stats and the beginning of the RX RUNT
|
||||
*/
|
||||
#define UMAC_MIB_STAT_OFFSET 0xc
|
||||
|
||||
#define UMAC_MIB_CTRL 0x580
|
||||
#define MIB_RX_CNT_RST (1 << 0)
|
||||
#define MIB_RUNT_CNT_RST (1 << 1)
|
||||
#define MIB_TX_CNT_RST (1 << 2)
|
||||
#define UMAC_MDF_CTRL 0x650
|
||||
#define UMAC_MDF_ADDR 0x654
|
||||
|
||||
/* Receive DMA offset and defines */
|
||||
#define SYS_PORT_RDMA_OFFSET 0x2000
|
||||
|
||||
#define RDMA_CONTROL 0x1000
|
||||
#define RDMA_EN (1 << 0)
|
||||
#define RDMA_RING_CFG (1 << 1)
|
||||
#define RDMA_DISC_EN (1 << 2)
|
||||
#define RDMA_BUF_DATA_OFFSET_SHIFT 4
|
||||
#define RDMA_BUF_DATA_OFFSET_MASK 0x3ff
|
||||
|
||||
#define RDMA_STATUS 0x1004
|
||||
#define RDMA_DISABLED (1 << 0)
|
||||
#define RDMA_DESC_RAM_INIT_BUSY (1 << 1)
|
||||
#define RDMA_BP_STATUS (1 << 2)
|
||||
|
||||
#define RDMA_SCB_BURST_SIZE 0x1008
|
||||
|
||||
#define RDMA_RING_BUF_SIZE 0x100c
|
||||
#define RDMA_RING_SIZE_SHIFT 16
|
||||
|
||||
#define RDMA_WRITE_PTR_HI 0x1010
|
||||
#define RDMA_WRITE_PTR_LO 0x1014
|
||||
#define RDMA_PROD_INDEX 0x1018
|
||||
#define RDMA_PROD_INDEX_MASK 0xffff
|
||||
|
||||
#define RDMA_CONS_INDEX 0x101c
|
||||
#define RDMA_CONS_INDEX_MASK 0xffff
|
||||
|
||||
#define RDMA_START_ADDR_HI 0x1020
|
||||
#define RDMA_START_ADDR_LO 0x1024
|
||||
#define RDMA_END_ADDR_HI 0x1028
|
||||
#define RDMA_END_ADDR_LO 0x102c
|
||||
|
||||
#define RDMA_MBDONE_INTR 0x1030
|
||||
#define RDMA_INTR_THRESH_MASK 0xff
|
||||
#define RDMA_TIMEOUT_SHIFT 16
|
||||
#define RDMA_TIMEOUT_MASK 0xffff
|
||||
|
||||
#define RDMA_XON_XOFF_THRESH 0x1034
|
||||
#define RDMA_XON_XOFF_THRESH_MASK 0xffff
|
||||
#define RDMA_XOFF_THRESH_SHIFT 16
|
||||
|
||||
#define RDMA_READ_PTR_HI 0x1038
|
||||
#define RDMA_READ_PTR_LO 0x103c
|
||||
|
||||
#define RDMA_OVERRIDE 0x1040
|
||||
#define RDMA_LE_MODE (1 << 0)
|
||||
#define RDMA_REG_MODE (1 << 1)
|
||||
|
||||
#define RDMA_TEST 0x1044
|
||||
#define RDMA_TP_OUT_SEL (1 << 0)
|
||||
#define RDMA_MEM_SEL (1 << 1)
|
||||
|
||||
#define RDMA_DEBUG 0x1048
|
||||
|
||||
/* Transmit DMA offset and defines */
|
||||
#define TDMA_NUM_RINGS 32 /* rings = queues */
|
||||
#define TDMA_PORT_SIZE DESC_SIZE /* two 32-bits words */
|
||||
|
||||
#define SYS_PORT_TDMA_OFFSET 0x4000
|
||||
#define TDMA_WRITE_PORT_OFFSET 0x0000
|
||||
#define TDMA_WRITE_PORT_HI(i) (TDMA_WRITE_PORT_OFFSET + \
|
||||
(i) * TDMA_PORT_SIZE)
|
||||
#define TDMA_WRITE_PORT_LO(i) (TDMA_WRITE_PORT_OFFSET + \
|
||||
sizeof(u32) + (i) * TDMA_PORT_SIZE)
|
||||
|
||||
#define TDMA_READ_PORT_OFFSET (TDMA_WRITE_PORT_OFFSET + \
|
||||
(TDMA_NUM_RINGS * TDMA_PORT_SIZE))
|
||||
#define TDMA_READ_PORT_HI(i) (TDMA_READ_PORT_OFFSET + \
|
||||
(i) * TDMA_PORT_SIZE)
|
||||
#define TDMA_READ_PORT_LO(i) (TDMA_READ_PORT_OFFSET + \
|
||||
sizeof(u32) + (i) * TDMA_PORT_SIZE)
|
||||
|
||||
#define TDMA_READ_PORT_CMD_OFFSET (TDMA_READ_PORT_OFFSET + \
|
||||
(TDMA_NUM_RINGS * TDMA_PORT_SIZE))
|
||||
#define TDMA_READ_PORT_CMD(i) (TDMA_READ_PORT_CMD_OFFSET + \
|
||||
(i) * sizeof(u32))
|
||||
|
||||
#define TDMA_DESC_RING_00_BASE (TDMA_READ_PORT_CMD_OFFSET + \
|
||||
(TDMA_NUM_RINGS * sizeof(u32)))
|
||||
|
||||
/* Register offsets and defines relatives to a specific ring number */
|
||||
#define RING_HEAD_TAIL_PTR 0x00
|
||||
#define RING_HEAD_MASK 0x7ff
|
||||
#define RING_TAIL_SHIFT 11
|
||||
#define RING_TAIL_MASK 0x7ff
|
||||
#define RING_FLUSH (1 << 24)
|
||||
#define RING_EN (1 << 25)
|
||||
|
||||
#define RING_COUNT 0x04
|
||||
#define RING_COUNT_MASK 0x7ff
|
||||
#define RING_BUFF_DONE_SHIFT 11
|
||||
#define RING_BUFF_DONE_MASK 0x7ff
|
||||
|
||||
#define RING_MAX_HYST 0x08
|
||||
#define RING_MAX_THRESH_MASK 0x7ff
|
||||
#define RING_HYST_THRESH_SHIFT 11
|
||||
#define RING_HYST_THRESH_MASK 0x7ff
|
||||
|
||||
#define RING_INTR_CONTROL 0x0c
|
||||
#define RING_INTR_THRESH_MASK 0x7ff
|
||||
#define RING_EMPTY_INTR_EN (1 << 15)
|
||||
#define RING_TIMEOUT_SHIFT 16
|
||||
#define RING_TIMEOUT_MASK 0xffff
|
||||
|
||||
#define RING_PROD_CONS_INDEX 0x10
|
||||
#define RING_PROD_INDEX_MASK 0xffff
|
||||
#define RING_CONS_INDEX_SHIFT 16
|
||||
#define RING_CONS_INDEX_MASK 0xffff
|
||||
|
||||
#define RING_MAPPING 0x14
|
||||
#define RING_QID_MASK 0x3
|
||||
#define RING_PORT_ID_SHIFT 3
|
||||
#define RING_PORT_ID_MASK 0x7
|
||||
#define RING_IGNORE_STATUS (1 << 6)
|
||||
#define RING_FAILOVER_EN (1 << 7)
|
||||
#define RING_CREDIT_SHIFT 8
|
||||
#define RING_CREDIT_MASK 0xffff
|
||||
|
||||
#define RING_PCP_DEI_VID 0x18
|
||||
#define RING_VID_MASK 0x7ff
|
||||
#define RING_DEI (1 << 12)
|
||||
#define RING_PCP_SHIFT 13
|
||||
#define RING_PCP_MASK 0x7
|
||||
#define RING_PKT_SIZE_ADJ_SHIFT 16
|
||||
#define RING_PKT_SIZE_ADJ_MASK 0xf
|
||||
|
||||
#define TDMA_DESC_RING_SIZE 28
|
||||
|
||||
/* Defininition for a given TX ring base address */
|
||||
#define TDMA_DESC_RING_BASE(i) (TDMA_DESC_RING_00_BASE + \
|
||||
((i) * TDMA_DESC_RING_SIZE))
|
||||
|
||||
/* Ring indexed register addreses */
|
||||
#define TDMA_DESC_RING_HEAD_TAIL_PTR(i) (TDMA_DESC_RING_BASE(i) + \
|
||||
RING_HEAD_TAIL_PTR)
|
||||
#define TDMA_DESC_RING_COUNT(i) (TDMA_DESC_RING_BASE(i) + \
|
||||
RING_COUNT)
|
||||
#define TDMA_DESC_RING_MAX_HYST(i) (TDMA_DESC_RING_BASE(i) + \
|
||||
RING_MAX_HYST)
|
||||
#define TDMA_DESC_RING_INTR_CONTROL(i) (TDMA_DESC_RING_BASE(i) + \
|
||||
RING_INTR_CONTROL)
|
||||
#define TDMA_DESC_RING_PROD_CONS_INDEX(i) \
|
||||
(TDMA_DESC_RING_BASE(i) + \
|
||||
RING_PROD_CONS_INDEX)
|
||||
#define TDMA_DESC_RING_MAPPING(i) (TDMA_DESC_RING_BASE(i) + \
|
||||
RING_MAPPING)
|
||||
#define TDMA_DESC_RING_PCP_DEI_VID(i) (TDMA_DESC_RING_BASE(i) + \
|
||||
RING_PCP_DEI_VID)
|
||||
|
||||
#define TDMA_CONTROL 0x600
|
||||
#define TDMA_EN (1 << 0)
|
||||
#define TSB_EN (1 << 1)
|
||||
#define TSB_SWAP (1 << 2)
|
||||
#define ACB_ALGO (1 << 3)
|
||||
#define BUF_DATA_OFFSET_SHIFT 4
|
||||
#define BUF_DATA_OFFSET_MASK 0x3ff
|
||||
#define VLAN_EN (1 << 14)
|
||||
#define SW_BRCM_TAG (1 << 15)
|
||||
#define WNC_KPT_SIZE_UPDATE (1 << 16)
|
||||
#define SYNC_PKT_SIZE (1 << 17)
|
||||
#define ACH_TXDONE_DELAY_SHIFT 18
|
||||
#define ACH_TXDONE_DELAY_MASK 0xff
|
||||
|
||||
#define TDMA_STATUS 0x604
|
||||
#define TDMA_DISABLED (1 << 0)
|
||||
#define TDMA_LL_RAM_INIT_BUSY (1 << 1)
|
||||
|
||||
#define TDMA_SCB_BURST_SIZE 0x608
|
||||
#define TDMA_OVER_MAX_THRESH_STATUS 0x60c
|
||||
#define TDMA_OVER_HYST_THRESH_STATUS 0x610
|
||||
#define TDMA_TPID 0x614
|
||||
|
||||
#define TDMA_FREE_LIST_HEAD_TAIL_PTR 0x618
|
||||
#define TDMA_FREE_HEAD_MASK 0x7ff
|
||||
#define TDMA_FREE_TAIL_SHIFT 11
|
||||
#define TDMA_FREE_TAIL_MASK 0x7ff
|
||||
|
||||
#define TDMA_FREE_LIST_COUNT 0x61c
|
||||
#define TDMA_FREE_LIST_COUNT_MASK 0x7ff
|
||||
|
||||
#define TDMA_TIER2_ARB_CTRL 0x620
|
||||
#define TDMA_ARB_MODE_RR 0
|
||||
#define TDMA_ARB_MODE_WEIGHT_RR 0x1
|
||||
#define TDMA_ARB_MODE_STRICT 0x2
|
||||
#define TDMA_ARB_MODE_DEFICIT_RR 0x3
|
||||
#define TDMA_CREDIT_SHIFT 4
|
||||
#define TDMA_CREDIT_MASK 0xffff
|
||||
|
||||
#define TDMA_TIER1_ARB_0_CTRL 0x624
|
||||
#define TDMA_ARB_EN (1 << 0)
|
||||
|
||||
#define TDMA_TIER1_ARB_0_QUEUE_EN 0x628
|
||||
#define TDMA_TIER1_ARB_1_CTRL 0x62c
|
||||
#define TDMA_TIER1_ARB_1_QUEUE_EN 0x630
|
||||
#define TDMA_TIER1_ARB_2_CTRL 0x634
|
||||
#define TDMA_TIER1_ARB_2_QUEUE_EN 0x638
|
||||
#define TDMA_TIER1_ARB_3_CTRL 0x63c
|
||||
#define TDMA_TIER1_ARB_3_QUEUE_EN 0x640
|
||||
|
||||
#define TDMA_SCB_ENDIAN_OVERRIDE 0x644
|
||||
#define TDMA_LE_MODE (1 << 0)
|
||||
#define TDMA_REG_MODE (1 << 1)
|
||||
|
||||
#define TDMA_TEST 0x648
|
||||
#define TDMA_TP_OUT_SEL (1 << 0)
|
||||
#define TDMA_MEM_TM (1 << 1)
|
||||
|
||||
#define TDMA_DEBUG 0x64c
|
||||
|
||||
/* Transmit/Receive descriptor */
|
||||
struct dma_desc {
|
||||
u32 addr_status_len;
|
||||
u32 addr_lo;
|
||||
};
|
||||
|
||||
/* Number of Receive hardware descriptor words */
|
||||
#define NUM_HW_RX_DESC_WORDS 1024
|
||||
/* Real number of usable descriptors */
|
||||
#define NUM_RX_DESC (NUM_HW_RX_DESC_WORDS / WORDS_PER_DESC)
|
||||
|
||||
/* Internal linked-list RAM has up to 1536 entries */
|
||||
#define NUM_TX_DESC 1536
|
||||
|
||||
#define WORDS_PER_DESC (sizeof(struct dma_desc) / sizeof(u32))
|
||||
|
||||
/* Rx/Tx common counter group.*/
|
||||
struct bcm_sysport_pkt_counters {
|
||||
u32 cnt_64; /* RO Received/Transmited 64 bytes packet */
|
||||
u32 cnt_127; /* RO Rx/Tx 127 bytes packet */
|
||||
u32 cnt_255; /* RO Rx/Tx 65-255 bytes packet */
|
||||
u32 cnt_511; /* RO Rx/Tx 256-511 bytes packet */
|
||||
u32 cnt_1023; /* RO Rx/Tx 512-1023 bytes packet */
|
||||
u32 cnt_1518; /* RO Rx/Tx 1024-1518 bytes packet */
|
||||
u32 cnt_mgv; /* RO Rx/Tx 1519-1522 good VLAN packet */
|
||||
u32 cnt_2047; /* RO Rx/Tx 1522-2047 bytes packet*/
|
||||
u32 cnt_4095; /* RO Rx/Tx 2048-4095 bytes packet*/
|
||||
u32 cnt_9216; /* RO Rx/Tx 4096-9216 bytes packet*/
|
||||
};
|
||||
|
||||
/* RSV, Receive Status Vector */
|
||||
struct bcm_sysport_rx_counters {
|
||||
struct bcm_sysport_pkt_counters pkt_cnt;
|
||||
u32 pkt; /* RO (0x428) Received pkt count*/
|
||||
u32 bytes; /* RO Received byte count */
|
||||
u32 mca; /* RO # of Received multicast pkt */
|
||||
u32 bca; /* RO # of Receive broadcast pkt */
|
||||
u32 fcs; /* RO # of Received FCS error */
|
||||
u32 cf; /* RO # of Received control frame pkt*/
|
||||
u32 pf; /* RO # of Received pause frame pkt */
|
||||
u32 uo; /* RO # of unknown op code pkt */
|
||||
u32 aln; /* RO # of alignment error count */
|
||||
u32 flr; /* RO # of frame length out of range count */
|
||||
u32 cde; /* RO # of code error pkt */
|
||||
u32 fcr; /* RO # of carrier sense error pkt */
|
||||
u32 ovr; /* RO # of oversize pkt*/
|
||||
u32 jbr; /* RO # of jabber count */
|
||||
u32 mtue; /* RO # of MTU error pkt*/
|
||||
u32 pok; /* RO # of Received good pkt */
|
||||
u32 uc; /* RO # of unicast pkt */
|
||||
u32 ppp; /* RO # of PPP pkt */
|
||||
u32 rcrc; /* RO (0x470),# of CRC match pkt */
|
||||
};
|
||||
|
||||
/* TSV, Transmit Status Vector */
|
||||
struct bcm_sysport_tx_counters {
|
||||
struct bcm_sysport_pkt_counters pkt_cnt;
|
||||
u32 pkts; /* RO (0x4a8) Transmited pkt */
|
||||
u32 mca; /* RO # of xmited multicast pkt */
|
||||
u32 bca; /* RO # of xmited broadcast pkt */
|
||||
u32 pf; /* RO # of xmited pause frame count */
|
||||
u32 cf; /* RO # of xmited control frame count */
|
||||
u32 fcs; /* RO # of xmited FCS error count */
|
||||
u32 ovr; /* RO # of xmited oversize pkt */
|
||||
u32 drf; /* RO # of xmited deferral pkt */
|
||||
u32 edf; /* RO # of xmited Excessive deferral pkt*/
|
||||
u32 scl; /* RO # of xmited single collision pkt */
|
||||
u32 mcl; /* RO # of xmited multiple collision pkt*/
|
||||
u32 lcl; /* RO # of xmited late collision pkt */
|
||||
u32 ecl; /* RO # of xmited excessive collision pkt*/
|
||||
u32 frg; /* RO # of xmited fragments pkt*/
|
||||
u32 ncl; /* RO # of xmited total collision count */
|
||||
u32 jbr; /* RO # of xmited jabber count*/
|
||||
u32 bytes; /* RO # of xmited byte count */
|
||||
u32 pok; /* RO # of xmited good pkt */
|
||||
u32 uc; /* RO (0x0x4f0)# of xmited unitcast pkt */
|
||||
};
|
||||
|
||||
struct bcm_sysport_mib {
|
||||
struct bcm_sysport_rx_counters rx;
|
||||
struct bcm_sysport_tx_counters tx;
|
||||
u32 rx_runt_cnt;
|
||||
u32 rx_runt_fcs;
|
||||
u32 rx_runt_fcs_align;
|
||||
u32 rx_runt_bytes;
|
||||
u32 rxchk_bad_csum;
|
||||
u32 rxchk_other_pkt_disc;
|
||||
u32 rbuf_ovflow_cnt;
|
||||
u32 rbuf_err_cnt;
|
||||
};
|
||||
|
||||
/* HW maintains a large list of counters */
|
||||
enum bcm_sysport_stat_type {
|
||||
BCM_SYSPORT_STAT_NETDEV = -1,
|
||||
BCM_SYSPORT_STAT_MIB_RX,
|
||||
BCM_SYSPORT_STAT_MIB_TX,
|
||||
BCM_SYSPORT_STAT_RUNT,
|
||||
BCM_SYSPORT_STAT_RXCHK,
|
||||
BCM_SYSPORT_STAT_RBUF,
|
||||
};
|
||||
|
||||
/* Macros to help define ethtool statistics */
|
||||
#define STAT_NETDEV(m) { \
|
||||
.stat_string = __stringify(m), \
|
||||
.stat_sizeof = sizeof(((struct net_device_stats *)0)->m), \
|
||||
.stat_offset = offsetof(struct net_device_stats, m), \
|
||||
.type = BCM_SYSPORT_STAT_NETDEV, \
|
||||
}
|
||||
|
||||
#define STAT_MIB(str, m, _type) { \
|
||||
.stat_string = str, \
|
||||
.stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
|
||||
.stat_offset = offsetof(struct bcm_sysport_priv, m), \
|
||||
.type = _type, \
|
||||
}
|
||||
|
||||
#define STAT_MIB_RX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_RX)
|
||||
#define STAT_MIB_TX(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_MIB_TX)
|
||||
#define STAT_RUNT(str, m) STAT_MIB(str, m, BCM_SYSPORT_STAT_RUNT)
|
||||
|
||||
#define STAT_RXCHK(str, m, ofs) { \
|
||||
.stat_string = str, \
|
||||
.stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
|
||||
.stat_offset = offsetof(struct bcm_sysport_priv, m), \
|
||||
.type = BCM_SYSPORT_STAT_RXCHK, \
|
||||
.reg_offset = ofs, \
|
||||
}
|
||||
|
||||
#define STAT_RBUF(str, m, ofs) { \
|
||||
.stat_string = str, \
|
||||
.stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \
|
||||
.stat_offset = offsetof(struct bcm_sysport_priv, m), \
|
||||
.type = BCM_SYSPORT_STAT_RBUF, \
|
||||
.reg_offset = ofs, \
|
||||
}
|
||||
|
||||
struct bcm_sysport_stats {
|
||||
char stat_string[ETH_GSTRING_LEN];
|
||||
int stat_sizeof;
|
||||
int stat_offset;
|
||||
enum bcm_sysport_stat_type type;
|
||||
/* reg offset from UMAC base for misc counters */
|
||||
u16 reg_offset;
|
||||
};
|
||||
|
||||
/* Software house keeping helper structure */
|
||||
struct bcm_sysport_cb {
|
||||
struct sk_buff *skb; /* SKB for RX packets */
|
||||
void __iomem *bd_addr; /* Buffer descriptor PHYS addr */
|
||||
|
||||
DEFINE_DMA_UNMAP_ADDR(dma_addr);
|
||||
DEFINE_DMA_UNMAP_LEN(dma_len);
|
||||
};
|
||||
|
||||
/* Software view of the TX ring */
|
||||
struct bcm_sysport_tx_ring {
|
||||
spinlock_t lock; /* Ring lock for tx reclaim/xmit */
|
||||
struct napi_struct napi; /* NAPI per tx queue */
|
||||
dma_addr_t desc_dma; /* DMA cookie */
|
||||
unsigned int index; /* Ring index */
|
||||
unsigned int size; /* Ring current size */
|
||||
unsigned int alloc_size; /* Ring one-time allocated size */
|
||||
unsigned int desc_count; /* Number of descriptors */
|
||||
unsigned int curr_desc; /* Current descriptor */
|
||||
unsigned int c_index; /* Last consumer index */
|
||||
unsigned int p_index; /* Current producer index */
|
||||
struct bcm_sysport_cb *cbs; /* Transmit control blocks */
|
||||
struct dma_desc *desc_cpu; /* CPU view of the descriptor */
|
||||
struct bcm_sysport_priv *priv; /* private context backpointer */
|
||||
};
|
||||
|
||||
/* Driver private structure */
|
||||
struct bcm_sysport_priv {
|
||||
void __iomem *base;
|
||||
u32 irq0_stat;
|
||||
u32 irq0_mask;
|
||||
u32 irq1_stat;
|
||||
u32 irq1_mask;
|
||||
struct napi_struct napi ____cacheline_aligned;
|
||||
struct net_device *netdev;
|
||||
struct platform_device *pdev;
|
||||
int irq0;
|
||||
int irq1;
|
||||
|
||||
/* Transmit rings */
|
||||
struct bcm_sysport_tx_ring tx_rings[TDMA_NUM_RINGS];
|
||||
|
||||
/* Receive queue */
|
||||
void __iomem *rx_bds;
|
||||
void __iomem *rx_bd_assign_ptr;
|
||||
unsigned int rx_bd_assign_index;
|
||||
struct bcm_sysport_cb *rx_cbs;
|
||||
unsigned int num_rx_bds;
|
||||
unsigned int rx_read_ptr;
|
||||
unsigned int rx_c_index;
|
||||
|
||||
/* PHY device */
|
||||
struct device_node *phy_dn;
|
||||
struct phy_device *phydev;
|
||||
phy_interface_t phy_interface;
|
||||
int old_pause;
|
||||
int old_link;
|
||||
int old_duplex;
|
||||
|
||||
/* Misc fields */
|
||||
unsigned int rx_csum_en:1;
|
||||
unsigned int tsb_en:1;
|
||||
unsigned int crc_fwd:1;
|
||||
u16 rev;
|
||||
|
||||
/* MIB related fields */
|
||||
struct bcm_sysport_mib mib;
|
||||
|
||||
/* Ethtool */
|
||||
u32 msg_enable;
|
||||
};
|
||||
#endif /* __BCM_SYSPORT_H */
|
||||
|
|
@ -1436,7 +1436,7 @@ static int bgmac_probe(struct bcma_device *core)
|
|||
return -ENOMEM;
|
||||
net_dev->netdev_ops = &bgmac_netdev_ops;
|
||||
net_dev->irq = core->irq;
|
||||
SET_ETHTOOL_OPS(net_dev, &bgmac_ethtool_ops);
|
||||
net_dev->ethtool_ops = &bgmac_ethtool_ops;
|
||||
bgmac = netdev_priv(net_dev);
|
||||
bgmac->net_dev = net_dev;
|
||||
bgmac->core = core;
|
||||
|
|
|
|||
|
|
@ -6916,8 +6916,8 @@ bnx2_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|||
}
|
||||
}
|
||||
else {
|
||||
ethtool_cmd_speed_set(cmd, -1);
|
||||
cmd->duplex = -1;
|
||||
ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
|
||||
cmd->duplex = DUPLEX_UNKNOWN;
|
||||
}
|
||||
spin_unlock_bh(&bp->phy_lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Based on code from Michael Chan's bnx2 driver
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Based on code from Michael Chan's bnx2 driver
|
||||
* UDP CSUM errata workaround by Arik Gendelman
|
||||
|
|
@ -906,6 +906,18 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
|
|||
bd_prod = RX_BD(bd_prod);
|
||||
bd_cons = RX_BD(bd_cons);
|
||||
|
||||
/* A rmb() is required to ensure that the CQE is not read
|
||||
* before it is written by the adapter DMA. PCI ordering
|
||||
* rules will make sure the other fields are written before
|
||||
* the marker at the end of struct eth_fast_path_rx_cqe
|
||||
* but without rmb() a weakly ordered processor can process
|
||||
* stale data. Without the barrier TPA state-machine might
|
||||
* enter inconsistent state and kernel stack might be
|
||||
* provided with incorrect packet description - these lead
|
||||
* to various kernel crashed.
|
||||
*/
|
||||
rmb();
|
||||
|
||||
cqe_fp_flags = cqe_fp->type_error_flags;
|
||||
cqe_fp_type = cqe_fp_flags & ETH_FAST_PATH_RX_CQE_TYPE;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Based on code from Michael Chan's bnx2 driver
|
||||
* UDP CSUM errata workaround by Arik Gendelman
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Dmitry Kravkov
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Dmitry Kravkov
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Based on code from Michael Chan's bnx2 driver
|
||||
* UDP CSUM errata workaround by Arik Gendelman
|
||||
|
|
@ -3316,7 +3316,7 @@ static u32 bnx2x_get_rxfh_indir_size(struct net_device *dev)
|
|||
return T_ETH_INDIRECTION_TABLE_SIZE;
|
||||
}
|
||||
|
||||
static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
|
||||
static int bnx2x_get_rxfh(struct net_device *dev, u32 *indir, u8 *key)
|
||||
{
|
||||
struct bnx2x *bp = netdev_priv(dev);
|
||||
u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0};
|
||||
|
|
@ -3340,14 +3340,15 @@ static int bnx2x_get_rxfh_indir(struct net_device *dev, u32 *indir)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir)
|
||||
static int bnx2x_set_rxfh(struct net_device *dev, const u32 *indir,
|
||||
const u8 *key)
|
||||
{
|
||||
struct bnx2x *bp = netdev_priv(dev);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) {
|
||||
/*
|
||||
* The same as in bnx2x_get_rxfh_indir: we can't use a memcpy()
|
||||
* The same as in bnx2x_get_rxfh: we can't use a memcpy()
|
||||
* as an internal storage of an indirection table is a u8 array
|
||||
* while indir->ring_index points to an array of u32.
|
||||
*
|
||||
|
|
@ -3471,8 +3472,8 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
|
|||
.get_rxnfc = bnx2x_get_rxnfc,
|
||||
.set_rxnfc = bnx2x_set_rxnfc,
|
||||
.get_rxfh_indir_size = bnx2x_get_rxfh_indir_size,
|
||||
.get_rxfh_indir = bnx2x_get_rxfh_indir,
|
||||
.set_rxfh_indir = bnx2x_set_rxfh_indir,
|
||||
.get_rxfh = bnx2x_get_rxfh,
|
||||
.set_rxfh = bnx2x_set_rxfh,
|
||||
.get_channels = bnx2x_get_channels,
|
||||
.set_channels = bnx2x_set_channels,
|
||||
.get_module_info = bnx2x_get_module_info,
|
||||
|
|
@ -3498,16 +3499,14 @@ static const struct ethtool_ops bnx2x_vf_ethtool_ops = {
|
|||
.get_rxnfc = bnx2x_get_rxnfc,
|
||||
.set_rxnfc = bnx2x_set_rxnfc,
|
||||
.get_rxfh_indir_size = bnx2x_get_rxfh_indir_size,
|
||||
.get_rxfh_indir = bnx2x_get_rxfh_indir,
|
||||
.set_rxfh_indir = bnx2x_set_rxfh_indir,
|
||||
.get_rxfh = bnx2x_get_rxfh,
|
||||
.set_rxfh = bnx2x_set_rxfh,
|
||||
.get_channels = bnx2x_get_channels,
|
||||
.set_channels = bnx2x_set_channels,
|
||||
};
|
||||
|
||||
void bnx2x_set_ethtool_ops(struct bnx2x *bp, struct net_device *netdev)
|
||||
{
|
||||
if (IS_PF(bp))
|
||||
SET_ETHTOOL_OPS(netdev, &bnx2x_ethtool_ops);
|
||||
else /* vf */
|
||||
SET_ETHTOOL_OPS(netdev, &bnx2x_vf_ethtool_ops);
|
||||
netdev->ethtool_ops = (IS_PF(bp)) ?
|
||||
&bnx2x_ethtool_ops : &bnx2x_vf_ethtool_ops;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Written by: Vladislav Zolotarov <vladz@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Vladislav Zolotarov
|
||||
* Based on the original idea of John Wright <john.wright@hp.com>.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Modified by: Vladislav Zolotarov <vladz@broadcom.com>
|
||||
* Modified by: Vladislav Zolotarov
|
||||
*/
|
||||
|
||||
#ifndef BNX2X_INIT_H
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Written by: Vladislav Zolotarov <vladz@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Vladislav Zolotarov
|
||||
*/
|
||||
|
||||
#ifndef BNX2X_INIT_OPS_H
|
||||
|
|
|
|||
|
|
@ -2218,7 +2218,6 @@ int bnx2x_update_pfc(struct link_params *params,
|
|||
*/
|
||||
u32 val;
|
||||
struct bnx2x *bp = params->bp;
|
||||
int bnx2x_status = 0;
|
||||
u8 bmac_loopback = (params->loopback_mode == LOOPBACK_BMAC);
|
||||
|
||||
if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED)
|
||||
|
|
@ -2232,7 +2231,7 @@ int bnx2x_update_pfc(struct link_params *params,
|
|||
bnx2x_update_pfc_nig(params, vars, pfc_params);
|
||||
|
||||
if (!vars->link_up)
|
||||
return bnx2x_status;
|
||||
return 0;
|
||||
|
||||
DP(NETIF_MSG_LINK, "About to update PFC in BMAC\n");
|
||||
|
||||
|
|
@ -2246,7 +2245,7 @@ int bnx2x_update_pfc(struct link_params *params,
|
|||
== 0) {
|
||||
DP(NETIF_MSG_LINK, "About to update PFC in EMAC\n");
|
||||
bnx2x_emac_enable(params, vars, 0);
|
||||
return bnx2x_status;
|
||||
return 0;
|
||||
}
|
||||
if (CHIP_IS_E2(bp))
|
||||
bnx2x_update_pfc_bmac2(params, vars, bmac_loopback);
|
||||
|
|
@ -2260,7 +2259,7 @@ int bnx2x_update_pfc(struct link_params *params,
|
|||
val = 1;
|
||||
REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + params->port*4, val);
|
||||
}
|
||||
return bnx2x_status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bnx2x_bmac1_enable(struct link_params *params,
|
||||
|
|
@ -3703,7 +3702,8 @@ static void bnx2x_warpcore_restart_AN_KR(struct bnx2x_phy *phy,
|
|||
static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
|
||||
struct link_params *params,
|
||||
struct link_vars *vars) {
|
||||
u16 lane, i, cl72_ctrl, an_adv = 0;
|
||||
u16 lane, i, cl72_ctrl, an_adv = 0, val;
|
||||
u32 wc_lane_config;
|
||||
struct bnx2x *bp = params->bp;
|
||||
static struct bnx2x_reg_set reg_set[] = {
|
||||
{MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7},
|
||||
|
|
@ -3822,15 +3822,27 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy,
|
|||
/* Enable Auto-Detect to support 1G over CL37 as well */
|
||||
bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
|
||||
MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, 0x10);
|
||||
|
||||
wc_lane_config = REG_RD(bp, params->shmem_base +
|
||||
offsetof(struct shmem_region, dev_info.
|
||||
shared_hw_config.wc_lane_config));
|
||||
bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD,
|
||||
MDIO_WC_REG_RX0_PCI_CTRL + (lane << 4), &val);
|
||||
/* Force cl48 sync_status LOW to avoid getting stuck in CL73
|
||||
* parallel-detect loop when CL73 and CL37 are enabled.
|
||||
*/
|
||||
CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK,
|
||||
MDIO_AER_BLOCK_AER_REG, 0);
|
||||
val |= 1 << 11;
|
||||
|
||||
/* Restore Polarity settings in case it was run over by
|
||||
* previous link owner
|
||||
*/
|
||||
if (wc_lane_config &
|
||||
(SHARED_HW_CFG_RX_LANE0_POL_FLIP_ENABLED << lane))
|
||||
val |= 3 << 2;
|
||||
else
|
||||
val &= ~(3 << 2);
|
||||
bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD,
|
||||
MDIO_WC_REG_RXB_ANA_RX_CONTROL_PCI, 0x0800);
|
||||
bnx2x_set_aer_mmd(params, phy);
|
||||
MDIO_WC_REG_RX0_PCI_CTRL + (lane << 4),
|
||||
val);
|
||||
|
||||
bnx2x_disable_kr2(params, vars, phy);
|
||||
}
|
||||
|
|
@ -6473,7 +6485,6 @@ int bnx2x_test_link(struct link_params *params, struct link_vars *vars,
|
|||
static int bnx2x_link_initialize(struct link_params *params,
|
||||
struct link_vars *vars)
|
||||
{
|
||||
int rc = 0;
|
||||
u8 phy_index, non_ext_phy;
|
||||
struct bnx2x *bp = params->bp;
|
||||
/* In case of external phy existence, the line speed would be the
|
||||
|
|
@ -6546,7 +6557,7 @@ static int bnx2x_link_initialize(struct link_params *params,
|
|||
NIG_STATUS_XGXS0_LINK_STATUS |
|
||||
NIG_STATUS_SERDES0_LINK_STATUS |
|
||||
NIG_MASK_MI_INT));
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bnx2x_int_link_reset(struct bnx2x_phy *phy,
|
||||
|
|
@ -12461,6 +12472,7 @@ static int bnx2x_avoid_link_flap(struct link_params *params,
|
|||
u32 dont_clear_stat, lfa_sts;
|
||||
struct bnx2x *bp = params->bp;
|
||||
|
||||
bnx2x_set_mdio_emac_per_phy(bp, params);
|
||||
/* Sync the link parameters */
|
||||
bnx2x_link_status_update(params, vars);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Based on code from Michael Chan's bnx2 driver
|
||||
* UDP CSUM errata workaround by Arik Gendelman
|
||||
|
|
@ -10053,6 +10053,24 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp,
|
|||
#define BCM_5710_UNDI_FW_MF_VERS (0x05)
|
||||
#define BNX2X_PREV_UNDI_MF_PORT(p) (BAR_TSTRORM_INTMEM + 0x150c + ((p) << 4))
|
||||
#define BNX2X_PREV_UNDI_MF_FUNC(f) (BAR_TSTRORM_INTMEM + 0x184c + ((f) << 4))
|
||||
|
||||
static bool bnx2x_prev_is_after_undi(struct bnx2x *bp)
|
||||
{
|
||||
/* UNDI marks its presence in DORQ -
|
||||
* it initializes CID offset for normal bell to 0x7
|
||||
*/
|
||||
if (!(REG_RD(bp, MISC_REG_RESET_REG_1) &
|
||||
MISC_REGISTERS_RESET_REG_1_RST_DORQ))
|
||||
return false;
|
||||
|
||||
if (REG_RD(bp, DORQ_REG_NORM_CID_OFST) == 0x7) {
|
||||
BNX2X_DEV_INFO("UNDI previously loaded\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool bnx2x_prev_unload_undi_fw_supports_mf(struct bnx2x *bp)
|
||||
{
|
||||
u8 major, minor, version;
|
||||
|
|
@ -10302,6 +10320,10 @@ static int bnx2x_prev_unload_uncommon(struct bnx2x *bp)
|
|||
|
||||
BNX2X_DEV_INFO("Path is unmarked\n");
|
||||
|
||||
/* Cannot proceed with FLR if UNDI is loaded, since FW does not match */
|
||||
if (bnx2x_prev_is_after_undi(bp))
|
||||
goto out;
|
||||
|
||||
/* If function has FLR capabilities, and existing FW version matches
|
||||
* the one required, then FLR will be sufficient to clean any residue
|
||||
* left by previous driver
|
||||
|
|
@ -10322,6 +10344,7 @@ static int bnx2x_prev_unload_uncommon(struct bnx2x *bp)
|
|||
|
||||
BNX2X_DEV_INFO("Could not FLR\n");
|
||||
|
||||
out:
|
||||
/* Close the MCP request, return failure*/
|
||||
rc = bnx2x_prev_mcp_done(bp);
|
||||
if (!rc)
|
||||
|
|
@ -10360,19 +10383,13 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)
|
|||
/* close LLH filters towards the BRB */
|
||||
bnx2x_set_rx_filter(&bp->link_params, 0);
|
||||
|
||||
/* Check if the UNDI driver was previously loaded
|
||||
* UNDI driver initializes CID offset for normal bell to 0x7
|
||||
*/
|
||||
if (reset_reg & MISC_REGISTERS_RESET_REG_1_RST_DORQ) {
|
||||
tmp_reg = REG_RD(bp, DORQ_REG_NORM_CID_OFST);
|
||||
if (tmp_reg == 0x7) {
|
||||
BNX2X_DEV_INFO("UNDI previously loaded\n");
|
||||
prev_undi = true;
|
||||
/* clear the UNDI indication */
|
||||
REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0);
|
||||
/* clear possible idle check errors */
|
||||
REG_RD(bp, NIG_REG_NIG_INT_STS_CLR_0);
|
||||
}
|
||||
/* Check if the UNDI driver was previously loaded */
|
||||
if (bnx2x_prev_is_after_undi(bp)) {
|
||||
prev_undi = true;
|
||||
/* clear the UNDI indication */
|
||||
REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0);
|
||||
/* clear possible idle check errors */
|
||||
REG_RD(bp, NIG_REG_NIG_INT_STS_CLR_0);
|
||||
}
|
||||
if (!CHIP_IS_E1x(bp))
|
||||
/* block FW from writing to host */
|
||||
|
|
@ -13283,8 +13300,8 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
|
|||
netdev_reset_tc(bp->dev);
|
||||
|
||||
del_timer_sync(&bp->timer);
|
||||
cancel_delayed_work(&bp->sp_task);
|
||||
cancel_delayed_work(&bp->period_task);
|
||||
cancel_delayed_work_sync(&bp->sp_task);
|
||||
cancel_delayed_work_sync(&bp->period_task);
|
||||
|
||||
spin_lock_bh(&bp->stats_lock);
|
||||
bp->stats_state = STATS_STATE_DISABLED;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Vladislav Zolotarov
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Vladislav Zolotarov
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Written by: Shmulik Ravid <shmulikr@broadcom.com>
|
||||
* Ariel Elior <ariele@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Shmulik Ravid
|
||||
* Ariel Elior <ariel.elior@qlogic.com>
|
||||
*
|
||||
*/
|
||||
#include "bnx2x.h"
|
||||
|
|
@ -1071,8 +1071,10 @@ void bnx2x_iov_init_dq(struct bnx2x *bp)
|
|||
REG_WR(bp, DORQ_REG_VF_TYPE_MIN_MCID_0, 0);
|
||||
REG_WR(bp, DORQ_REG_VF_TYPE_MAX_MCID_0, 0x1ffff);
|
||||
|
||||
/* set the VF doorbell threshold */
|
||||
REG_WR(bp, DORQ_REG_VF_USAGE_CT_LIMIT, 4);
|
||||
/* set the VF doorbell threshold. This threshold represents the amount
|
||||
* of doorbells allowed in the main DORQ fifo for a specific VF.
|
||||
*/
|
||||
REG_WR(bp, DORQ_REG_VF_USAGE_CT_LIMIT, 64);
|
||||
}
|
||||
|
||||
void bnx2x_iov_init_dmae(struct bnx2x *bp)
|
||||
|
|
@ -2576,7 +2578,8 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
|
|||
|
||||
ivi->vf = vfidx;
|
||||
ivi->qos = 0;
|
||||
ivi->tx_rate = 10000; /* always 10G. TBA take from link struct */
|
||||
ivi->max_tx_rate = 10000; /* always 10G. TBA take from link struct */
|
||||
ivi->min_tx_rate = 0;
|
||||
ivi->spoofchk = 1; /*always enabled */
|
||||
if (vf->state == VF_ENABLED) {
|
||||
/* mac and vlan are in vlan_mac objects */
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Written by: Shmulik Ravid <shmulikr@broadcom.com>
|
||||
* Ariel Elior <ariele@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Shmulik Ravid
|
||||
* Ariel Elior <ariel.elior@qlogic.com>
|
||||
*/
|
||||
#ifndef BNX2X_SRIOV_H
|
||||
#define BNX2X_SRIOV_H
|
||||
|
|
@ -571,7 +571,7 @@ static inline void __iomem *bnx2x_vf_doorbells(struct bnx2x *bp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline void bnx2x_vf_pci_dealloc(struct bnx2 *bp) {return 0; }
|
||||
static inline void bnx2x_vf_pci_dealloc(struct bnx2x *bp) {}
|
||||
static inline int bnx2x_vf_pci_alloc(struct bnx2x *bp) {return 0; }
|
||||
static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {}
|
||||
static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; }
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Based on code from Michael Chan's bnx2 driver
|
||||
* UDP CSUM errata workaround by Arik Gendelman
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Eliezer Tamir
|
||||
* Based on code from Michael Chan's bnx2 driver
|
||||
* UDP CSUM errata workaround by Arik Gendelman
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Written by: Shmulik Ravid <shmulikr@broadcom.com>
|
||||
* Ariel Elior <ariele@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Shmulik Ravid
|
||||
* Ariel Elior <ariel.elior@qlogic.com>
|
||||
*/
|
||||
|
||||
#include "bnx2x.h"
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@
|
|||
* license other than the GPL, without Broadcom's express prior written
|
||||
* consent.
|
||||
*
|
||||
* Maintained by: Eilon Greenstein <eilong@broadcom.com>
|
||||
* Written by: Ariel Elior <ariele@broadcom.com>
|
||||
* Maintained by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
* Written by: Ariel Elior <ariel.elior@qlogic.com>
|
||||
*/
|
||||
#ifndef VF_PF_IF_H
|
||||
#define VF_PF_IF_H
|
||||
|
|
|
|||
|
|
@ -608,6 +608,10 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
|
|||
pr_err("%s: Bad type %d\n", __func__, ulp_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ulp_type == CNIC_ULP_ISCSI)
|
||||
cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
|
||||
|
||||
mutex_lock(&cnic_lock);
|
||||
if (rcu_dereference(cp->ulp_ops[ulp_type])) {
|
||||
RCU_INIT_POINTER(cp->ulp_ops[ulp_type], NULL);
|
||||
|
|
@ -620,9 +624,7 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
|
|||
}
|
||||
mutex_unlock(&cnic_lock);
|
||||
|
||||
if (ulp_type == CNIC_ULP_ISCSI)
|
||||
cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL);
|
||||
else if (ulp_type == CNIC_ULP_FCOE)
|
||||
if (ulp_type == CNIC_ULP_FCOE)
|
||||
dev->fcoe_cap = NULL;
|
||||
|
||||
synchronize_rcu();
|
||||
|
|
@ -1039,21 +1041,17 @@ static int cnic_alloc_uio_rings(struct cnic_dev *dev, int pages)
|
|||
struct cnic_local *cp = dev->cnic_priv;
|
||||
struct cnic_uio_dev *udev;
|
||||
|
||||
read_lock(&cnic_dev_lock);
|
||||
list_for_each_entry(udev, &cnic_udev_list, list) {
|
||||
if (udev->pdev == dev->pcidev) {
|
||||
udev->dev = dev;
|
||||
if (__cnic_alloc_uio_rings(udev, pages)) {
|
||||
udev->dev = NULL;
|
||||
read_unlock(&cnic_dev_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
cp->udev = udev;
|
||||
read_unlock(&cnic_dev_lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
read_unlock(&cnic_dev_lock);
|
||||
|
||||
udev = kzalloc(sizeof(struct cnic_uio_dev), GFP_ATOMIC);
|
||||
if (!udev)
|
||||
|
|
@ -1067,9 +1065,7 @@ static int cnic_alloc_uio_rings(struct cnic_dev *dev, int pages)
|
|||
if (__cnic_alloc_uio_rings(udev, pages))
|
||||
goto err_udev;
|
||||
|
||||
write_lock(&cnic_dev_lock);
|
||||
list_add(&udev->list, &cnic_udev_list);
|
||||
write_unlock(&cnic_dev_lock);
|
||||
|
||||
pci_dev_get(udev->pdev);
|
||||
|
||||
|
|
@ -5624,20 +5620,27 @@ static void cnic_rcv_netevent(struct cnic_local *cp, unsigned long event,
|
|||
{
|
||||
int if_type;
|
||||
|
||||
rcu_read_lock();
|
||||
for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
|
||||
struct cnic_ulp_ops *ulp_ops;
|
||||
void *ctx;
|
||||
|
||||
ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
|
||||
if (!ulp_ops || !ulp_ops->indicate_netevent)
|
||||
mutex_lock(&cnic_lock);
|
||||
ulp_ops = rcu_dereference_protected(cp->ulp_ops[if_type],
|
||||
lockdep_is_held(&cnic_lock));
|
||||
if (!ulp_ops || !ulp_ops->indicate_netevent) {
|
||||
mutex_unlock(&cnic_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx = cp->ulp_handle[if_type];
|
||||
|
||||
set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
|
||||
mutex_unlock(&cnic_lock);
|
||||
|
||||
ulp_ops->indicate_netevent(ctx, event, vlan_id);
|
||||
|
||||
clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/* netdev event handler */
|
||||
|
|
|
|||
|
|
@ -2481,7 +2481,7 @@ static int bcmgenet_probe(struct platform_device *pdev)
|
|||
dev_set_drvdata(&pdev->dev, dev);
|
||||
ether_addr_copy(dev->dev_addr, macaddr);
|
||||
dev->watchdog_timeo = 2 * HZ;
|
||||
SET_ETHTOOL_OPS(dev, &bcmgenet_ethtool_ops);
|
||||
dev->ethtool_ops = &bcmgenet_ethtool_ops;
|
||||
dev->netdev_ops = &bcmgenet_netdev_ops;
|
||||
netif_napi_add(dev, &priv->napi, bcmgenet_poll, 64);
|
||||
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ int bcmgenet_mii_config(struct net_device *dev)
|
|||
static int bcmgenet_mii_probe(struct net_device *dev)
|
||||
{
|
||||
struct bcmgenet_priv *priv = netdev_priv(dev);
|
||||
struct device_node *dn = priv->pdev->dev.of_node;
|
||||
struct phy_device *phydev;
|
||||
unsigned int phy_flags;
|
||||
int ret;
|
||||
|
|
@ -307,15 +308,19 @@ static int bcmgenet_mii_probe(struct net_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (priv->phy_dn)
|
||||
phydev = of_phy_connect(dev, priv->phy_dn,
|
||||
bcmgenet_mii_setup, 0,
|
||||
priv->phy_interface);
|
||||
else
|
||||
phydev = of_phy_connect_fixed_link(dev,
|
||||
bcmgenet_mii_setup,
|
||||
priv->phy_interface);
|
||||
/* In the case of a fixed PHY, the DT node associated
|
||||
* to the PHY is the Ethernet MAC DT node.
|
||||
*/
|
||||
if (of_phy_is_fixed_link(dn)) {
|
||||
ret = of_phy_register_fixed_link(dn);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->phy_dn = dn;
|
||||
}
|
||||
|
||||
phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup, 0,
|
||||
priv->phy_interface);
|
||||
if (!phydev) {
|
||||
pr_err("could not attach to PHY\n");
|
||||
return -ENODEV;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com)
|
||||
* Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com)
|
||||
* Copyright (C) 2004 Sun Microsystems Inc.
|
||||
* Copyright (C) 2005-2013 Broadcom Corporation.
|
||||
* Copyright (C) 2005-2014 Broadcom Corporation.
|
||||
*
|
||||
* Firmware is:
|
||||
* Derived from proprietary unpublished source code,
|
||||
|
|
@ -94,10 +94,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
|
|||
|
||||
#define DRV_MODULE_NAME "tg3"
|
||||
#define TG3_MAJ_NUM 3
|
||||
#define TG3_MIN_NUM 136
|
||||
#define TG3_MIN_NUM 137
|
||||
#define DRV_MODULE_VERSION \
|
||||
__stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
|
||||
#define DRV_MODULE_RELDATE "Jan 03, 2014"
|
||||
#define DRV_MODULE_RELDATE "May 11, 2014"
|
||||
|
||||
#define RESET_KIND_SHUTDOWN 0
|
||||
#define RESET_KIND_INIT 1
|
||||
|
|
@ -3224,7 +3224,7 @@ static int tg3_nvram_read_using_eeprom(struct tg3 *tp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define NVRAM_CMD_TIMEOUT 10000
|
||||
#define NVRAM_CMD_TIMEOUT 100
|
||||
|
||||
static int tg3_nvram_exec_cmd(struct tg3 *tp, u32 nvram_cmd)
|
||||
{
|
||||
|
|
@ -7871,9 +7871,7 @@ tg3_tso_bug_end:
|
|||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
/* hard_start_xmit for devices that have the 4G bug and/or 40-bit bug and
|
||||
* support TG3_FLAG_HW_TSO_1 or firmware TSO only.
|
||||
*/
|
||||
/* hard_start_xmit for all devices */
|
||||
static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct tg3 *tp = netdev_priv(dev);
|
||||
|
|
@ -7884,6 +7882,10 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
struct tg3_napi *tnapi;
|
||||
struct netdev_queue *txq;
|
||||
unsigned int last;
|
||||
struct iphdr *iph = NULL;
|
||||
struct tcphdr *tcph = NULL;
|
||||
__sum16 tcp_csum = 0, ip_csum = 0;
|
||||
__be16 ip_tot_len = 0;
|
||||
|
||||
txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
|
||||
tnapi = &tp->napi[skb_get_queue_mapping(skb)];
|
||||
|
|
@ -7915,7 +7917,6 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
|
||||
mss = skb_shinfo(skb)->gso_size;
|
||||
if (mss) {
|
||||
struct iphdr *iph;
|
||||
u32 tcp_opt_len, hdr_len;
|
||||
|
||||
if (skb_cow_head(skb, 0))
|
||||
|
|
@ -7927,27 +7928,31 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb) - ETH_HLEN;
|
||||
|
||||
if (!skb_is_gso_v6(skb)) {
|
||||
if (unlikely((ETH_HLEN + hdr_len) > 80) &&
|
||||
tg3_flag(tp, TSO_BUG))
|
||||
return tg3_tso_bug(tp, skb);
|
||||
|
||||
ip_csum = iph->check;
|
||||
ip_tot_len = iph->tot_len;
|
||||
iph->check = 0;
|
||||
iph->tot_len = htons(mss + hdr_len);
|
||||
}
|
||||
|
||||
if (unlikely((ETH_HLEN + hdr_len) > 80) &&
|
||||
tg3_flag(tp, TSO_BUG))
|
||||
return tg3_tso_bug(tp, skb);
|
||||
|
||||
base_flags |= (TXD_FLAG_CPU_PRE_DMA |
|
||||
TXD_FLAG_CPU_POST_DMA);
|
||||
|
||||
tcph = tcp_hdr(skb);
|
||||
tcp_csum = tcph->check;
|
||||
|
||||
if (tg3_flag(tp, HW_TSO_1) ||
|
||||
tg3_flag(tp, HW_TSO_2) ||
|
||||
tg3_flag(tp, HW_TSO_3)) {
|
||||
tcp_hdr(skb)->check = 0;
|
||||
tcph->check = 0;
|
||||
base_flags &= ~TXD_FLAG_TCPUDP_CSUM;
|
||||
} else
|
||||
tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
|
||||
iph->daddr, 0,
|
||||
IPPROTO_TCP,
|
||||
0);
|
||||
} else {
|
||||
tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
|
||||
0, IPPROTO_TCP, 0);
|
||||
}
|
||||
|
||||
if (tg3_flag(tp, HW_TSO_3)) {
|
||||
mss |= (hdr_len & 0xc) << 12;
|
||||
|
|
@ -8047,6 +8052,18 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev)
|
|||
if (would_hit_hwbug) {
|
||||
tg3_tx_skb_unmap(tnapi, tnapi->tx_prod, i);
|
||||
|
||||
if (mss) {
|
||||
/* If it's a TSO packet, do GSO instead of
|
||||
* allocating and copying to a large linear SKB
|
||||
*/
|
||||
if (ip_tot_len) {
|
||||
iph->check = ip_csum;
|
||||
iph->tot_len = ip_tot_len;
|
||||
}
|
||||
tcph->check = tcp_csum;
|
||||
return tg3_tso_bug(tp, skb);
|
||||
}
|
||||
|
||||
/* If the workaround fails due to memory/mapping
|
||||
* failure, silently drop this packet.
|
||||
*/
|
||||
|
|
@ -11876,9 +11893,9 @@ static int tg3_get_eeprom_len(struct net_device *dev)
|
|||
static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
|
||||
{
|
||||
struct tg3 *tp = netdev_priv(dev);
|
||||
int ret;
|
||||
int ret, cpmu_restore = 0;
|
||||
u8 *pd;
|
||||
u32 i, offset, len, b_offset, b_count;
|
||||
u32 i, offset, len, b_offset, b_count, cpmu_val = 0;
|
||||
__be32 val;
|
||||
|
||||
if (tg3_flag(tp, NO_NVRAM))
|
||||
|
|
@ -11890,6 +11907,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
|
|||
|
||||
eeprom->magic = TG3_EEPROM_MAGIC;
|
||||
|
||||
/* Override clock, link aware and link idle modes */
|
||||
if (tg3_flag(tp, CPMU_PRESENT)) {
|
||||
cpmu_val = tr32(TG3_CPMU_CTRL);
|
||||
if (cpmu_val & (CPMU_CTRL_LINK_AWARE_MODE |
|
||||
CPMU_CTRL_LINK_IDLE_MODE)) {
|
||||
tw32(TG3_CPMU_CTRL, cpmu_val &
|
||||
~(CPMU_CTRL_LINK_AWARE_MODE |
|
||||
CPMU_CTRL_LINK_IDLE_MODE));
|
||||
cpmu_restore = 1;
|
||||
}
|
||||
}
|
||||
tg3_override_clk(tp);
|
||||
|
||||
if (offset & 3) {
|
||||
/* adjustments to start on required 4 byte boundary */
|
||||
b_offset = offset & 3;
|
||||
|
|
@ -11900,7 +11930,7 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
|
|||
}
|
||||
ret = tg3_nvram_read_be32(tp, offset-b_offset, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto eeprom_done;
|
||||
memcpy(data, ((char *)&val) + b_offset, b_count);
|
||||
len -= b_count;
|
||||
offset += b_count;
|
||||
|
|
@ -11912,10 +11942,20 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
|
|||
for (i = 0; i < (len - (len & 3)); i += 4) {
|
||||
ret = tg3_nvram_read_be32(tp, offset + i, &val);
|
||||
if (ret) {
|
||||
if (i)
|
||||
i -= 4;
|
||||
eeprom->len += i;
|
||||
return ret;
|
||||
goto eeprom_done;
|
||||
}
|
||||
memcpy(pd + i, &val, 4);
|
||||
if (need_resched()) {
|
||||
if (signal_pending(current)) {
|
||||
eeprom->len += i;
|
||||
ret = -EINTR;
|
||||
goto eeprom_done;
|
||||
}
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
eeprom->len += i;
|
||||
|
||||
|
|
@ -11926,11 +11966,19 @@ static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom,
|
|||
b_offset = offset + len - b_count;
|
||||
ret = tg3_nvram_read_be32(tp, b_offset, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto eeprom_done;
|
||||
memcpy(pd, &val, b_count);
|
||||
eeprom->len += b_count;
|
||||
}
|
||||
return 0;
|
||||
ret = 0;
|
||||
|
||||
eeprom_done:
|
||||
/* Restore clock, link aware and link idle modes */
|
||||
tg3_restore_clk(tp);
|
||||
if (cpmu_restore)
|
||||
tw32(TG3_CPMU_CTRL, cpmu_val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tg3_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
|
||||
|
|
@ -12484,7 +12532,7 @@ static u32 tg3_get_rxfh_indir_size(struct net_device *dev)
|
|||
return size;
|
||||
}
|
||||
|
||||
static int tg3_get_rxfh_indir(struct net_device *dev, u32 *indir)
|
||||
static int tg3_get_rxfh(struct net_device *dev, u32 *indir, u8 *key)
|
||||
{
|
||||
struct tg3 *tp = netdev_priv(dev);
|
||||
int i;
|
||||
|
|
@ -12495,7 +12543,7 @@ static int tg3_get_rxfh_indir(struct net_device *dev, u32 *indir)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tg3_set_rxfh_indir(struct net_device *dev, const u32 *indir)
|
||||
static int tg3_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key)
|
||||
{
|
||||
struct tg3 *tp = netdev_priv(dev);
|
||||
size_t i;
|
||||
|
|
@ -14027,8 +14075,8 @@ static const struct ethtool_ops tg3_ethtool_ops = {
|
|||
.get_sset_count = tg3_get_sset_count,
|
||||
.get_rxnfc = tg3_get_rxnfc,
|
||||
.get_rxfh_indir_size = tg3_get_rxfh_indir_size,
|
||||
.get_rxfh_indir = tg3_get_rxfh_indir,
|
||||
.set_rxfh_indir = tg3_set_rxfh_indir,
|
||||
.get_rxfh = tg3_get_rxfh,
|
||||
.set_rxfh = tg3_set_rxfh,
|
||||
.get_channels = tg3_get_channels,
|
||||
.set_channels = tg3_set_channels,
|
||||
.get_ts_info = tg3_get_ts_info,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com)
|
||||
* Copyright (C) 2001 Jeff Garzik (jgarzik@pobox.com)
|
||||
* Copyright (C) 2004 Sun Microsystems Inc.
|
||||
* Copyright (C) 2007-2013 Broadcom Corporation.
|
||||
* Copyright (C) 2007-2014 Broadcom Corporation.
|
||||
*/
|
||||
|
||||
#ifndef _T3_H
|
||||
|
|
|
|||
|
|
@ -266,8 +266,8 @@ bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
|
|||
ethtool_cmd_speed_set(cmd, SPEED_10000);
|
||||
cmd->duplex = DUPLEX_FULL;
|
||||
} else {
|
||||
ethtool_cmd_speed_set(cmd, -1);
|
||||
cmd->duplex = -1;
|
||||
ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
|
||||
cmd->duplex = DUPLEX_UNKNOWN;
|
||||
}
|
||||
cmd->transceiver = XCVR_EXTERNAL;
|
||||
cmd->maxtxpkt = 0;
|
||||
|
|
@ -1137,5 +1137,5 @@ static const struct ethtool_ops bnad_ethtool_ops = {
|
|||
void
|
||||
bnad_set_ethtool_ops(struct net_device *netdev)
|
||||
{
|
||||
SET_ETHTOOL_OPS(netdev, &bnad_ethtool_ops);
|
||||
netdev->ethtool_ops = &bnad_ethtool_ops;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1737,7 +1737,7 @@ static int xgmac_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, ndev);
|
||||
ether_setup(ndev);
|
||||
ndev->netdev_ops = &xgmac_netdev_ops;
|
||||
SET_ETHTOOL_OPS(ndev, &xgmac_ethtool_ops);
|
||||
ndev->ethtool_ops = &xgmac_ethtool_ops;
|
||||
spin_lock_init(&priv->stats_lock);
|
||||
INIT_WORK(&priv->tx_timeout_work, xgmac_tx_timeout_work);
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue