diff options
Diffstat (limited to 'drivers/net')
195 files changed, 2597 insertions, 32183 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index edaab759dc50..8ec98f6dfef9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -503,8 +503,6 @@ source "drivers/net/arcnet/Kconfig" source "drivers/atm/Kconfig" -source "drivers/net/caif/Kconfig" - source "drivers/net/dsa/Kconfig" source "drivers/net/ethernet/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5b01215f6829..b87a741fc952 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -48,14 +48,12 @@ obj-$(CONFIG_MHI_NET) += mhi_net.o # Networking Drivers # obj-$(CONFIG_ARCNET) += arcnet/ -obj-$(CONFIG_CAIF) += caif/ obj-$(CONFIG_CAN) += can/ ifdef CONFIG_NET_DSA obj-y += dsa/ endif obj-$(CONFIG_ETHERNET) += ethernet/ obj-$(CONFIG_FDDI) += fddi/ -obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_QCOM_IPA) += ipa/ obj-$(CONFIG_PLIP) += plip/ obj-$(CONFIG_PPP) += ppp/ diff --git a/drivers/net/Space.c b/drivers/net/Space.c index c01e2c2f7d6c..305f0a712a64 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -200,24 +200,9 @@ static int __init probe_list2(int unit, struct devprobe2 *p, int autoprobe) * look for EISA/PCI cards in addition to ISA cards). */ static struct devprobe2 isa_probes[] __initdata = { -#ifdef CONFIG_3C515 - {tc515_probe, 0}, -#endif -#ifdef CONFIG_ULTRA - {ultra_probe, 0}, -#endif -#ifdef CONFIG_WD80x3 - {wd_probe, 0}, -#endif #if defined(CONFIG_NE2000) /* ISA (use ne2k-pci for PCI cards) */ {ne_probe, 0}, #endif -#ifdef CONFIG_LANCE /* ISA/VLB (use pcnet32 for PCI cards) */ - {lance_probe, 0}, -#endif -#ifdef CONFIG_SMC9194 - {smc_init, 0}, -#endif #ifdef CONFIG_CS89x0_ISA {cs89x0_probe, 0}, #endif diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index 0df3208783ad..da5866ba0699 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -529,6 +529,9 @@ static int bareudp_fill_metadata_dst(struct net_device *dev, struct in6_addr saddr; struct socket *sock = rcu_dereference(bareudp->sock); + if (!sock) + return -ESHUTDOWN; + dst = udp_tunnel6_dst_lookup(skb, dev, bareudp->net, sock, 0, &saddr, &info->key, sport, bareudp->port, info->key.tos, diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index af7f74cfdc08..f0aa7d2f2171 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1029,6 +1029,7 @@ static void ad_cond_set_peer_notif(struct port *port) static void ad_mux_machine(struct port *port, bool *update_slave_arr) { struct bonding *bond = __get_bond_by_port(port); + struct aggregator *aggregator; mux_states_t last_state; /* keep current State Machine state to compare later if it was @@ -1036,6 +1037,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) */ last_state = port->sm_mux_state; + aggregator = rcu_dereference(port->aggregator); if (port->sm_vars & AD_PORT_BEGIN) { port->sm_mux_state = AD_MUX_DETACHED; } else { @@ -1055,7 +1057,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * cycle to update ready variable, we check * READY_N and update READY here */ - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); port->sm_mux_state = AD_MUX_DETACHED; break; } @@ -1070,7 +1072,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * update ready variable, we check READY_N and update * READY here */ - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); /* if the wait_while_timer expired, and the port is * in READY state, move to ATTACHED state @@ -1086,7 +1088,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper.port_state & LACP_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) { - if (port->aggregator->is_active) { + if (aggregator->is_active) { int state = AD_MUX_COLLECTING_DISTRIBUTING; if (!bond->params.coupled_control) @@ -1102,9 +1104,9 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * cycle to update ready variable, we check * READY_N and update READY here */ - __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); port->sm_mux_state = AD_MUX_DETACHED; - } else if (port->aggregator->is_active) { + } else if (aggregator->is_active) { port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; } @@ -1115,7 +1117,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * sure that a collecting distributing * port in an active aggregator is enabled */ - if (port->aggregator->is_active && + if (aggregator->is_active && !__port_is_collecting_distributing(port)) { __enable_port(port); *update_slave_arr = true; @@ -1134,7 +1136,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) */ struct slave *slave = port->slave; - if (port->aggregator->is_active && + if (aggregator->is_active && bond_is_slave_rx_disabled(slave)) { ad_enable_collecting(port); *update_slave_arr = true; @@ -1154,8 +1156,8 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) * sure that a collecting distributing * port in an active aggregator is enabled */ - if (port->aggregator && - port->aggregator->is_active && + if (aggregator && + aggregator->is_active && !__port_is_collecting_distributing(port)) { __enable_port(port); *update_slave_arr = true; @@ -1187,7 +1189,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0); break; case AD_MUX_ATTACHED: - if (port->aggregator->is_active) + if (aggregator->is_active) port->actor_oper_port_state |= LACP_STATE_SYNCHRONIZATION; else @@ -1561,9 +1563,9 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) bond = __get_bond_by_port(port); /* if the port is connected to other aggregator, detach it */ - if (port->aggregator) { + temp_aggregator = rcu_dereference(port->aggregator); + if (temp_aggregator) { /* detach the port from its former aggregator */ - temp_aggregator = port->aggregator; for (curr_port = temp_aggregator->lag_ports; curr_port; last_port = curr_port, curr_port = curr_port->next_port_in_aggregator) { @@ -1586,7 +1588,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) /* clear the port's relations to this * aggregator */ - port->aggregator = NULL; + RCU_INIT_POINTER(port->aggregator, NULL); port->next_port_in_aggregator = NULL; port->actor_port_aggregator_identifier = 0; @@ -1609,7 +1611,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) port->slave->bond->dev->name, port->slave->dev->name, port->actor_port_number, - port->aggregator->aggregator_identifier); + temp_aggregator->aggregator_identifier); } } /* search on all aggregators for a suitable aggregator for this port */ @@ -1633,15 +1635,15 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) ) ) { /* attach to the founded aggregator */ - port->aggregator = aggregator; + rcu_assign_pointer(port->aggregator, aggregator); port->actor_port_aggregator_identifier = - port->aggregator->aggregator_identifier; + aggregator->aggregator_identifier; port->next_port_in_aggregator = aggregator->lag_ports; - port->aggregator->num_of_ports++; + aggregator->num_of_ports++; aggregator->lag_ports = port; slave_dbg(bond->dev, slave->dev, "Port %d joined LAG %d (existing LAG)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); /* mark this port as selected */ port->sm_vars |= AD_PORT_SELECTED; @@ -1656,39 +1658,40 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) if (!found) { if (free_aggregator) { /* assign port a new aggregator */ - port->aggregator = free_aggregator; port->actor_port_aggregator_identifier = - port->aggregator->aggregator_identifier; + free_aggregator->aggregator_identifier; /* update the new aggregator's parameters * if port was responsed from the end-user */ if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS) /* if port is full duplex */ - port->aggregator->is_individual = false; + free_aggregator->is_individual = false; else - port->aggregator->is_individual = true; + free_aggregator->is_individual = true; - port->aggregator->actor_admin_aggregator_key = + free_aggregator->actor_admin_aggregator_key = port->actor_admin_port_key; - port->aggregator->actor_oper_aggregator_key = + free_aggregator->actor_oper_aggregator_key = port->actor_oper_port_key; - port->aggregator->partner_system = + free_aggregator->partner_system = port->partner_oper.system; - port->aggregator->partner_system_priority = + free_aggregator->partner_system_priority = port->partner_oper.system_priority; - port->aggregator->partner_oper_aggregator_key = port->partner_oper.key; - port->aggregator->receive_state = 1; - port->aggregator->transmit_state = 1; - port->aggregator->lag_ports = port; - port->aggregator->num_of_ports++; + free_aggregator->partner_oper_aggregator_key = port->partner_oper.key; + free_aggregator->receive_state = 1; + free_aggregator->transmit_state = 1; + free_aggregator->lag_ports = port; + free_aggregator->num_of_ports++; + + rcu_assign_pointer(port->aggregator, free_aggregator); /* mark this port as selected */ port->sm_vars |= AD_PORT_SELECTED; slave_dbg(bond->dev, port->slave->dev, "Port %d joined LAG %d (new LAG)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + free_aggregator->aggregator_identifier); } else { slave_err(bond->dev, port->slave->dev, "Port %d did not find a suitable aggregator\n", @@ -1700,13 +1703,12 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) * in all aggregator's ports, else set ready=FALSE in all * aggregator's ports */ - __set_agg_ports_ready(port->aggregator, - __agg_ports_are_ready(port->aggregator)); + aggregator = rcu_dereference(port->aggregator); + __set_agg_ports_ready(aggregator, __agg_ports_are_ready(aggregator)); - aggregator = __get_first_agg(port); - ad_agg_selection_logic(aggregator, update_slave_arr); + ad_agg_selection_logic(__get_first_agg(port), update_slave_arr); - if (!port->aggregator->is_active) + if (!aggregator->is_active) port->actor_oper_port_state &= ~LACP_STATE_SYNCHRONIZATION; } @@ -2075,13 +2077,15 @@ static void ad_initialize_port(struct port *port, const struct bond_params *bond */ static void ad_enable_collecting(struct port *port) { - if (port->aggregator->is_active) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator->is_active) { struct slave *slave = port->slave; slave_dbg(slave->bond->dev, slave->dev, "Enabling collecting on port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __enable_collecting_port(port); } } @@ -2093,11 +2097,13 @@ static void ad_enable_collecting(struct port *port) */ static void ad_disable_distributing(struct port *port, bool *update_slave_arr) { - if (port->aggregator && __agg_has_partner(port->aggregator)) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator && __agg_has_partner(aggregator)) { slave_dbg(port->slave->bond->dev, port->slave->dev, "Disabling distributing on port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __disable_distributing_port(port); /* Slave array needs an update */ *update_slave_arr = true; @@ -2114,11 +2120,13 @@ static void ad_disable_distributing(struct port *port, bool *update_slave_arr) static void ad_enable_collecting_distributing(struct port *port, bool *update_slave_arr) { - if (port->aggregator->is_active) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator->is_active) { slave_dbg(port->slave->bond->dev, port->slave->dev, "Enabling port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __enable_port(port); /* Slave array needs update */ *update_slave_arr = true; @@ -2135,11 +2143,13 @@ static void ad_enable_collecting_distributing(struct port *port, static void ad_disable_collecting_distributing(struct port *port, bool *update_slave_arr) { - if (port->aggregator && __agg_has_partner(port->aggregator)) { + struct aggregator *aggregator = rcu_dereference(port->aggregator); + + if (aggregator && __agg_has_partner(aggregator)) { slave_dbg(port->slave->bond->dev, port->slave->dev, "Disabling port %d (LAG %d)\n", port->actor_port_number, - port->aggregator->aggregator_identifier); + aggregator->aggregator_identifier); __disable_port(port); /* Slave array needs an update */ *update_slave_arr = true; @@ -2379,7 +2389,7 @@ void bond_3ad_unbind_slave(struct slave *slave) */ for (temp_port = aggregator->lag_ports; temp_port; temp_port = temp_port->next_port_in_aggregator) { - temp_port->aggregator = new_aggregator; + rcu_assign_pointer(temp_port->aggregator, new_aggregator); temp_port->actor_port_aggregator_identifier = new_aggregator->aggregator_identifier; } @@ -2848,15 +2858,16 @@ out: int __bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) { - struct aggregator *aggregator = NULL; + struct aggregator *aggregator = NULL, *tmp; struct list_head *iter; struct slave *slave; struct port *port; bond_for_each_slave_rcu(bond, slave, iter) { port = &(SLAVE_AD_INFO(slave)->port); - if (port->aggregator && port->aggregator->is_active) { - aggregator = port->aggregator; + tmp = rcu_dereference(port->aggregator); + if (tmp && tmp->is_active) { + aggregator = tmp; break; } } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c7baa5c4bf40..af82a3df2c5d 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1433,7 +1433,7 @@ static void bond_poll_controller(struct net_device *bond_dev) if (BOND_MODE(bond) == BOND_MODE_8023AD) { struct aggregator *agg = - SLAVE_AD_INFO(slave)->port.aggregator; + rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); if (agg && agg->aggregator_identifier != ad_info.aggregator_id) @@ -5179,15 +5179,16 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) spin_unlock_bh(&bond->mode_lock); agg_id = ad_info.aggregator_id; } + rcu_read_lock(); bond_for_each_slave(bond, slave, iter) { if (skipslave == slave) continue; all_slaves->arr[all_slaves->count++] = slave; if (BOND_MODE(bond) == BOND_MODE_8023AD) { - struct aggregator *agg; + const struct aggregator *agg; - agg = SLAVE_AD_INFO(slave)->port.aggregator; + agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); if (!agg || agg->aggregator_identifier != agg_id) continue; } @@ -5199,6 +5200,7 @@ int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave) usable_slaves->arr[usable_slaves->count++] = slave; } + rcu_read_unlock(); bond_set_slave_arr(bond, usable_slaves, all_slaves); return ret; diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index ea1a80e658ae..c7d3e0602c83 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -66,27 +66,29 @@ static int bond_fill_slave_info(struct sk_buff *skb, const struct port *ad_port; ad_port = &SLAVE_AD_INFO(slave)->port; - agg = SLAVE_AD_INFO(slave)->port.aggregator; + rcu_read_lock(); + agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); if (agg) { if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_AGGREGATOR_ID, agg->aggregator_identifier)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE, ad_port->actor_oper_port_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u16(skb, IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE, ad_port->partner_oper.port_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_ACTOR_STATE, ad_port->sm_churn_actor_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; if (nla_put_u8(skb, IFLA_BOND_SLAVE_AD_CHURN_PARTNER_STATE, ad_port->sm_churn_partner_state)) - goto nla_put_failure; + goto nla_put_failure_rcu; } + rcu_read_unlock(); if (nla_put_u16(skb, IFLA_BOND_SLAVE_ACTOR_PORT_PRIO, SLAVE_AD_INFO(slave)->port_priority)) @@ -95,6 +97,8 @@ static int bond_fill_slave_info(struct sk_buff *skb, return 0; +nla_put_failure_rcu: + rcu_read_unlock(); nla_put_failure: return -EMSGSIZE; } diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index e34f80305191..3714aab1a3d9 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -188,6 +188,7 @@ static void bond_info_show_master(struct seq_file *seq) } } +/* Note: runs under rcu_read_lock() */ static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave) { @@ -214,7 +215,7 @@ static void bond_info_show_slave(struct seq_file *seq, if (BOND_MODE(bond) == BOND_MODE_8023AD) { const struct port *port = &SLAVE_AD_INFO(slave)->port; - const struct aggregator *agg = port->aggregator; + const struct aggregator *agg = rcu_dereference(port->aggregator); if (agg) { seq_printf(seq, "Aggregator ID: %d\n", diff --git a/drivers/net/bonding/bond_sysfs_slave.c b/drivers/net/bonding/bond_sysfs_slave.c index 36d0e8440b5b..fc6fe7181789 100644 --- a/drivers/net/bonding/bond_sysfs_slave.c +++ b/drivers/net/bonding/bond_sysfs_slave.c @@ -62,10 +62,15 @@ static ssize_t ad_aggregator_id_show(struct slave *slave, char *buf) const struct aggregator *agg; if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { - agg = SLAVE_AD_INFO(slave)->port.aggregator; - if (agg) - return sysfs_emit(buf, "%d\n", - agg->aggregator_identifier); + rcu_read_lock(); + agg = rcu_dereference(SLAVE_AD_INFO(slave)->port.aggregator); + if (agg) { + ssize_t res = sysfs_emit(buf, "%d\n", + agg->aggregator_identifier); + rcu_read_unlock(); + return res; + } + rcu_read_unlock(); } return sysfs_emit(buf, "N/A\n"); @@ -78,7 +83,7 @@ static ssize_t ad_actor_oper_port_state_show(struct slave *slave, char *buf) if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { ad_port = &SLAVE_AD_INFO(slave)->port; - if (ad_port->aggregator) + if (rcu_access_pointer(ad_port->aggregator)) return sysfs_emit(buf, "%u\n", ad_port->actor_oper_port_state); } @@ -93,7 +98,7 @@ static ssize_t ad_partner_oper_port_state_show(struct slave *slave, char *buf) if (BOND_MODE(slave->bond) == BOND_MODE_8023AD) { ad_port = &SLAVE_AD_INFO(slave)->port; - if (ad_port->aggregator) + if (rcu_access_pointer(ad_port->aggregator)) return sysfs_emit(buf, "%u\n", ad_port->partner_oper.port_state); } diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig deleted file mode 100644 index 709660cb38f8..000000000000 --- a/drivers/net/caif/Kconfig +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# CAIF physical drivers -# - -menuconfig CAIF_DRIVERS - bool "CAIF transport drivers" - depends on CAIF - help - Enable this to see CAIF physical drivers. - -if CAIF_DRIVERS - -config CAIF_TTY - tristate "CAIF TTY transport driver" - depends on CAIF && TTY - default n - help - The CAIF TTY transport driver is a Line Discipline (ldisc) - identified as N_CAIF. When this ldisc is opened from user space - it will redirect the TTY's traffic into the CAIF stack. - -config CAIF_VIRTIO - tristate "CAIF virtio transport driver" - depends on CAIF && HAS_DMA - select VHOST_RING - select VIRTIO - select GENERIC_ALLOCATOR - default n - help - The CAIF driver for CAIF over Virtio. - -endif # CAIF_DRIVERS diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile deleted file mode 100644 index 97f664f8016c..000000000000 --- a/drivers/net/caif/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG - -# Serial interface -obj-$(CONFIG_CAIF_TTY) += caif_serial.o - -# Virtio interface -obj-$(CONFIG_CAIF_VIRTIO) += caif_virtio.o diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c deleted file mode 100644 index 1873d8287bb9..000000000000 --- a/drivers/net/caif/caif_serial.c +++ /dev/null @@ -1,443 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2010 - * Author: Sjur Brendeland - */ - -#include <linux/hardirq.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/device.h> -#include <linux/types.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/rtnetlink.h> -#include <linux/tty.h> -#include <linux/file.h> -#include <linux/if_arp.h> -#include <net/caif/caif_device.h> -#include <net/caif/cfcnfg.h> -#include <linux/err.h> -#include <linux/debugfs.h> - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Sjur Brendeland"); -MODULE_DESCRIPTION("CAIF serial device TTY line discipline"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_CAIF); - -#define SEND_QUEUE_LOW 10 -#define SEND_QUEUE_HIGH 100 -#define CAIF_SENDING 1 /* Bit 1 = 0x02*/ -#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */ -#define MAX_WRITE_CHUNK 4096 -#define ON 1 -#define OFF 0 -#define CAIF_MAX_MTU 4096 - -static DEFINE_SPINLOCK(ser_lock); -static LIST_HEAD(ser_list); -static LIST_HEAD(ser_release_list); - -static bool ser_loop; -module_param(ser_loop, bool, 0444); -MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode."); - -static bool ser_use_stx = true; -module_param(ser_use_stx, bool, 0444); -MODULE_PARM_DESC(ser_use_stx, "STX enabled or not."); - -static bool ser_use_fcs = true; - -module_param(ser_use_fcs, bool, 0444); -MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not."); - -static int ser_write_chunk = MAX_WRITE_CHUNK; -module_param(ser_write_chunk, int, 0444); - -MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART."); - -static struct dentry *debugfsdir; - -static int caif_net_open(struct net_device *dev); -static int caif_net_close(struct net_device *dev); - -struct ser_device { - struct caif_dev_common common; - struct list_head node; - struct net_device *dev; - struct sk_buff_head head; - struct tty_struct *tty; - bool tx_started; - unsigned long state; -#ifdef CONFIG_DEBUG_FS - struct dentry *debugfs_tty_dir; - struct debugfs_blob_wrapper tx_blob; - struct debugfs_blob_wrapper rx_blob; - u8 rx_data[128]; - u8 tx_data[128]; - u8 tty_status; - -#endif -}; - -static void caifdev_setup(struct net_device *dev); -static void ldisc_tx_wakeup(struct tty_struct *tty); -#ifdef CONFIG_DEBUG_FS -static inline void update_tty_status(struct ser_device *ser) -{ - ser->tty_status = - ser->tty->flow.stopped << 5 | - ser->tty->flow.tco_stopped << 3 | - ser->tty->ctrl.packet << 2; -} -static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) -{ - ser->debugfs_tty_dir = debugfs_create_dir(tty->name, debugfsdir); - - debugfs_create_blob("last_tx_msg", 0400, ser->debugfs_tty_dir, - &ser->tx_blob); - - debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir, - &ser->rx_blob); - - debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir, - &ser->state); - - debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir, - &ser->tty_status); - - ser->tx_blob.data = ser->tx_data; - ser->tx_blob.size = 0; - ser->rx_blob.data = ser->rx_data; - ser->rx_blob.size = 0; -} - -static inline void debugfs_deinit(struct ser_device *ser) -{ - debugfs_remove_recursive(ser->debugfs_tty_dir); -} - -static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) -{ - if (size > sizeof(ser->rx_data)) - size = sizeof(ser->rx_data); - memcpy(ser->rx_data, data, size); - ser->rx_blob.data = ser->rx_data; - ser->rx_blob.size = size; -} -#else -static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) -{ -} - -static inline void debugfs_deinit(struct ser_device *ser) -{ -} - -static inline void update_tty_status(struct ser_device *ser) -{ -} - -static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size) -{ -} -#endif - -static void ldisc_receive(struct tty_struct *tty, const u8 *data, - const u8 *flags, size_t count) -{ - struct sk_buff *skb = NULL; - struct ser_device *ser; - int ret; - - ser = tty->disc_data; - - /* - * NOTE: flags may contain information about break or overrun. - * This is not yet handled. - */ - - - /* - * Workaround for garbage at start of transmission, - * only enable if STX handling is not enabled. - */ - if (!ser->common.use_stx && !ser->tx_started) { - dev_info(&ser->dev->dev, - "Bytes received before initial transmission -" - "bytes discarded.\n"); - return; - } - - BUG_ON(ser->dev == NULL); - - /* Get a suitable caif packet and copy in data. */ - skb = netdev_alloc_skb(ser->dev, count+1); - if (skb == NULL) - return; - skb_put_data(skb, data, count); - - skb->protocol = htons(ETH_P_CAIF); - skb_reset_mac_header(skb); - debugfs_rx(ser, data, count); - /* Push received packet up the stack. */ - ret = netif_rx(skb); - if (!ret) { - ser->dev->stats.rx_packets++; - ser->dev->stats.rx_bytes += count; - } else - ++ser->dev->stats.rx_dropped; - update_tty_status(ser); -} - -static int handle_tx(struct ser_device *ser) -{ - struct tty_struct *tty; - struct sk_buff *skb; - int tty_wr, len, room; - - tty = ser->tty; - ser->tx_started = true; - - /* Enter critical section */ - if (test_and_set_bit(CAIF_SENDING, &ser->state)) - return 0; - - /* skb_peek is safe because handle_tx is called after skb_queue_tail */ - while ((skb = skb_peek(&ser->head)) != NULL) { - - /* Make sure you don't write too much */ - len = skb->len; - room = tty_write_room(tty); - if (!room) - break; - if (room > ser_write_chunk) - room = ser_write_chunk; - if (len > room) - len = room; - - /* Write to tty or loopback */ - if (!ser_loop) { - tty_wr = tty->ops->write(tty, skb->data, len); - update_tty_status(ser); - } else { - tty_wr = len; - ldisc_receive(tty, skb->data, NULL, len); - } - ser->dev->stats.tx_packets++; - ser->dev->stats.tx_bytes += tty_wr; - - /* Error on TTY ?! */ - if (tty_wr < 0) - goto error; - /* Reduce buffer written, and discard if empty */ - skb_pull(skb, tty_wr); - if (skb->len == 0) { - struct sk_buff *tmp = skb_dequeue(&ser->head); - WARN_ON(tmp != skb); - dev_consume_skb_any(skb); - } - } - /* Send flow off if queue is empty */ - if (ser->head.qlen <= SEND_QUEUE_LOW && - test_and_clear_bit(CAIF_FLOW_OFF_SENT, &ser->state) && - ser->common.flowctrl != NULL) - ser->common.flowctrl(ser->dev, ON); - clear_bit(CAIF_SENDING, &ser->state); - return 0; -error: - clear_bit(CAIF_SENDING, &ser->state); - return tty_wr; -} - -static netdev_tx_t caif_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ser_device *ser; - - ser = netdev_priv(dev); - - /* Send flow off once, on high water mark */ - if (ser->head.qlen > SEND_QUEUE_HIGH && - !test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state) && - ser->common.flowctrl != NULL) - - ser->common.flowctrl(ser->dev, OFF); - - skb_queue_tail(&ser->head, skb); - return handle_tx(ser); -} - - -static void ldisc_tx_wakeup(struct tty_struct *tty) -{ - struct ser_device *ser; - - ser = tty->disc_data; - BUG_ON(ser == NULL); - WARN_ON(ser->tty != tty); - handle_tx(ser); -} - - -static void ser_release(struct work_struct *work) -{ - struct list_head list; - struct ser_device *ser, *tmp; - struct tty_struct *tty; - - spin_lock(&ser_lock); - list_replace_init(&ser_release_list, &list); - spin_unlock(&ser_lock); - - if (!list_empty(&list)) { - rtnl_lock(); - list_for_each_entry_safe(ser, tmp, &list, node) { - tty = ser->tty; - dev_close(ser->dev); - unregister_netdevice(ser->dev); - debugfs_deinit(ser); - tty_kref_put(tty->link); - tty_kref_put(tty); - } - rtnl_unlock(); - } -} - -static DECLARE_WORK(ser_release_work, ser_release); - -static int ldisc_open(struct tty_struct *tty) -{ - struct ser_device *ser; - struct net_device *dev; - char name[64]; - int result; - - /* No write no play */ - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG)) - return -EPERM; - - /* release devices to avoid name collision */ - ser_release(NULL); - - result = snprintf(name, sizeof(name), "cf%s", tty->name); - if (result >= IFNAMSIZ) - return -EINVAL; - dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN, - caifdev_setup); - if (!dev) - return -ENOMEM; - - ser = netdev_priv(dev); - ser->tty = tty_kref_get(tty); - tty_kref_get(tty->link); - ser->dev = dev; - debugfs_init(ser, tty); - tty->receive_room = 4096; - tty->disc_data = ser; - set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - rtnl_lock(); - result = register_netdevice(dev); - if (result) { - tty_kref_put(tty->link); - tty_kref_put(tty); - rtnl_unlock(); - free_netdev(dev); - return -ENODEV; - } - - spin_lock(&ser_lock); - list_add(&ser->node, &ser_list); - spin_unlock(&ser_lock); - rtnl_unlock(); - netif_stop_queue(dev); - update_tty_status(ser); - return 0; -} - -static void ldisc_close(struct tty_struct *tty) -{ - struct ser_device *ser = tty->disc_data; - - spin_lock(&ser_lock); - list_move(&ser->node, &ser_release_list); - spin_unlock(&ser_lock); - schedule_work(&ser_release_work); -} - -/* The line discipline structure. */ -static struct tty_ldisc_ops caif_ldisc = { - .owner = THIS_MODULE, - .num = N_CAIF, - .name = "n_caif", - .open = ldisc_open, - .close = ldisc_close, - .receive_buf = ldisc_receive, - .write_wakeup = ldisc_tx_wakeup -}; - -static const struct net_device_ops netdev_ops = { - .ndo_open = caif_net_open, - .ndo_stop = caif_net_close, - .ndo_start_xmit = caif_xmit -}; - -static void caifdev_setup(struct net_device *dev) -{ - struct ser_device *serdev = netdev_priv(dev); - - dev->features = 0; - dev->netdev_ops = &netdev_ops; - dev->type = ARPHRD_CAIF; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - dev->mtu = CAIF_MAX_MTU; - dev->priv_flags |= IFF_NO_QUEUE; - dev->needs_free_netdev = true; - skb_queue_head_init(&serdev->head); - serdev->common.link_select = CAIF_LINK_LOW_LATENCY; - serdev->common.use_frag = true; - serdev->common.use_stx = ser_use_stx; - serdev->common.use_fcs = ser_use_fcs; - serdev->dev = dev; -} - - -static int caif_net_open(struct net_device *dev) -{ - netif_wake_queue(dev); - return 0; -} - -static int caif_net_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static int __init caif_ser_init(void) -{ - int ret; - - ret = tty_register_ldisc(&caif_ldisc); - if (ret < 0) - pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, ret); - - debugfsdir = debugfs_create_dir("caif_serial", NULL); - return ret; -} - -static void __exit caif_ser_exit(void) -{ - spin_lock(&ser_lock); - list_splice(&ser_list, &ser_release_list); - spin_unlock(&ser_lock); - ser_release(NULL); - cancel_work_sync(&ser_release_work); - tty_unregister_ldisc(&caif_ldisc); - debugfs_remove_recursive(debugfsdir); -} - -module_init(caif_ser_init); -module_exit(caif_ser_exit); diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c deleted file mode 100644 index 8ac1a4b8e055..000000000000 --- a/drivers/net/caif/caif_virtio.c +++ /dev/null @@ -1,791 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson AB 2013 - * Authors: Vicram Arv - * Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> - * Sjur Brendeland - */ -#include <linux/module.h> -#include <linux/if_arp.h> -#include <linux/virtio.h> -#include <linux/vringh.h> -#include <linux/debugfs.h> -#include <linux/spinlock.h> -#include <linux/genalloc.h> -#include <linux/interrupt.h> -#include <linux/netdevice.h> -#include <linux/rtnetlink.h> -#include <linux/virtio_ids.h> -#include <linux/virtio_caif.h> -#include <linux/virtio_ring.h> -#include <linux/dma-mapping.h> -#include <net/caif/caif_dev.h> -#include <linux/virtio_config.h> - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Vicram Arv"); -MODULE_AUTHOR("Sjur Brendeland"); -MODULE_DESCRIPTION("Virtio CAIF Driver"); - -/* NAPI schedule quota */ -#define CFV_DEFAULT_QUOTA 32 - -/* Defaults used if virtio config space is unavailable */ -#define CFV_DEF_MTU_SIZE 4096 -#define CFV_DEF_HEADROOM 32 -#define CFV_DEF_TAILROOM 32 - -/* Required IP header alignment */ -#define IP_HDR_ALIGN 4 - -/* struct cfv_napi_contxt - NAPI context info - * @riov: IOV holding data read from the ring. Note that riov may - * still hold data when cfv_rx_poll() returns. - * @head: Last descriptor ID we received from vringh_getdesc_kern. - * We use this to put descriptor back on the used ring. USHRT_MAX is - * used to indicate invalid head-id. - */ -struct cfv_napi_context { - struct vringh_kiov riov; - unsigned short head; -}; - -/* struct cfv_stats - statistics for debugfs - * @rx_napi_complete: Number of NAPI completions (RX) - * @rx_napi_resched: Number of calls where the full quota was used (RX) - * @rx_nomem: Number of SKB alloc failures (RX) - * @rx_kicks: Number of RX kicks - * @tx_full_ring: Number times TX ring was full - * @tx_no_mem: Number of times TX went out of memory - * @tx_flow_on: Number of flow on (TX) - * @tx_kicks: Number of TX kicks - */ -struct cfv_stats { - u32 rx_napi_complete; - u32 rx_napi_resched; - u32 rx_nomem; - u32 rx_kicks; - u32 tx_full_ring; - u32 tx_no_mem; - u32 tx_flow_on; - u32 tx_kicks; -}; - -/* struct cfv_info - Caif Virtio control structure - * @cfdev: caif common header - * @vdev: Associated virtio device - * @vr_rx: rx/downlink host vring - * @vq_tx: tx/uplink virtqueue - * @ndev: CAIF link layer device - * @watermark_tx: indicates number of free descriptors we need - * to reopen the tx-queues after overload. - * @tx_lock: protects vq_tx from concurrent use - * @tx_release_tasklet: Tasklet for freeing consumed TX buffers - * @napi: Napi context used in cfv_rx_poll() - * @ctx: Context data used in cfv_rx_poll() - * @tx_hr: transmit headroom - * @rx_hr: receive headroom - * @tx_tr: transmit tail room - * @rx_tr: receive tail room - * @mtu: transmit max size - * @mru: receive max size - * @allocsz: size of dma memory reserved for TX buffers - * @alloc_addr: virtual address to dma memory for TX buffers - * @alloc_dma: dma address to dma memory for TX buffers - * @genpool: Gen Pool used for allocating TX buffers - * @reserved_mem: Pointer to memory reserve allocated from genpool - * @reserved_size: Size of memory reserve allocated from genpool - * @stats: Statistics exposed in sysfs - * @debugfs: Debugfs dentry for statistic counters - */ -struct cfv_info { - struct caif_dev_common cfdev; - struct virtio_device *vdev; - struct vringh *vr_rx; - struct virtqueue *vq_tx; - struct net_device *ndev; - unsigned int watermark_tx; - /* Protect access to vq_tx */ - spinlock_t tx_lock; - struct tasklet_struct tx_release_tasklet; - struct napi_struct napi; - struct cfv_napi_context ctx; - u16 tx_hr; - u16 rx_hr; - u16 tx_tr; - u16 rx_tr; - u32 mtu; - u32 mru; - size_t allocsz; - void *alloc_addr; - dma_addr_t alloc_dma; - struct gen_pool *genpool; - unsigned long reserved_mem; - size_t reserved_size; - struct cfv_stats stats; - struct dentry *debugfs; -}; - -/* struct buf_info - maintains transmit buffer data handle - * @size: size of transmit buffer - * @dma_handle: handle to allocated dma device memory area - * @vaddr: virtual address mapping to allocated memory area - */ -struct buf_info { - size_t size; - u8 *vaddr; -}; - -/* Called from virtio device, in IRQ context */ -static void cfv_release_cb(struct virtqueue *vq_tx) -{ - struct cfv_info *cfv = vq_tx->vdev->priv; - - ++cfv->stats.tx_kicks; - tasklet_schedule(&cfv->tx_release_tasklet); -} - -static void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info) -{ - if (!buf_info) - return; - gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr, - buf_info->size); - kfree(buf_info); -} - -/* This is invoked whenever the remote processor completed processing - * a TX msg we just sent, and the buffer is put back to the used ring. - */ -static void cfv_release_used_buf(struct virtqueue *vq_tx) -{ - struct cfv_info *cfv = vq_tx->vdev->priv; - unsigned long flags; - - BUG_ON(vq_tx != cfv->vq_tx); - - for (;;) { - unsigned int len; - struct buf_info *buf_info; - - /* Get used buffer from used ring to recycle used descriptors */ - spin_lock_irqsave(&cfv->tx_lock, flags); - buf_info = virtqueue_get_buf(vq_tx, &len); - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* Stop looping if there are no more buffers to free */ - if (!buf_info) - break; - - free_buf_info(cfv, buf_info); - - /* watermark_tx indicates if we previously stopped the tx - * queues. If we have enough free stots in the virtio ring, - * re-establish memory reserved and open up tx queues. - */ - if (cfv->vq_tx->num_free <= cfv->watermark_tx) - continue; - - /* Re-establish memory reserve */ - if (cfv->reserved_mem == 0 && cfv->genpool) - cfv->reserved_mem = - gen_pool_alloc(cfv->genpool, - cfv->reserved_size); - - /* Open up the tx queues */ - if (cfv->reserved_mem) { - cfv->watermark_tx = - virtqueue_get_vring_size(cfv->vq_tx); - netif_tx_wake_all_queues(cfv->ndev); - /* Buffers are recycled in cfv_netdev_tx, so - * disable notifications when queues are opened. - */ - virtqueue_disable_cb(cfv->vq_tx); - ++cfv->stats.tx_flow_on; - } else { - /* if no memory reserve, wait for more free slots */ - WARN_ON(cfv->watermark_tx > - virtqueue_get_vring_size(cfv->vq_tx)); - cfv->watermark_tx += - virtqueue_get_vring_size(cfv->vq_tx) / 4; - } - } -} - -/* Allocate a SKB and copy packet data to it */ -static struct sk_buff *cfv_alloc_and_copy_skb(int *err, - struct cfv_info *cfv, - u8 *frm, u32 frm_len) -{ - struct sk_buff *skb; - u32 cfpkt_len, pad_len; - - *err = 0; - /* Verify that packet size with down-link header and mtu size */ - if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) { - netdev_err(cfv->ndev, - "Invalid frmlen:%u mtu:%u hr:%d tr:%d\n", - frm_len, cfv->mru, cfv->rx_hr, - cfv->rx_tr); - *err = -EPROTO; - return NULL; - } - - cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr); - pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1); - - skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len); - if (!skb) { - *err = -ENOMEM; - return NULL; - } - - skb_reserve(skb, cfv->rx_hr + pad_len); - - skb_put_data(skb, frm + cfv->rx_hr, cfpkt_len); - return skb; -} - -/* Get packets from the host vring */ -static int cfv_rx_poll(struct napi_struct *napi, int quota) -{ - struct cfv_info *cfv = container_of(napi, struct cfv_info, napi); - int rxcnt = 0; - int err = 0; - void *buf; - struct sk_buff *skb; - struct vringh_kiov *riov = &cfv->ctx.riov; - unsigned int skb_len; - - do { - skb = NULL; - - /* Put the previous iovec back on the used ring and - * fetch a new iovec if we have processed all elements. - */ - if (riov->i == riov->used) { - if (cfv->ctx.head != USHRT_MAX) { - vringh_complete_kern(cfv->vr_rx, - cfv->ctx.head, - 0); - cfv->ctx.head = USHRT_MAX; - } - - err = vringh_getdesc_kern( - cfv->vr_rx, - riov, - NULL, - &cfv->ctx.head, - GFP_ATOMIC); - - if (err <= 0) - goto exit; - } - - buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base); - /* TODO: Add check on valid buffer address */ - - skb = cfv_alloc_and_copy_skb(&err, cfv, buf, - riov->iov[riov->i].iov_len); - if (unlikely(err)) - goto exit; - - /* Push received packet up the stack. */ - skb_len = skb->len; - skb->protocol = htons(ETH_P_CAIF); - skb_reset_mac_header(skb); - skb->dev = cfv->ndev; - err = netif_receive_skb(skb); - if (unlikely(err)) { - ++cfv->ndev->stats.rx_dropped; - } else { - ++cfv->ndev->stats.rx_packets; - cfv->ndev->stats.rx_bytes += skb_len; - } - - ++riov->i; - ++rxcnt; - } while (rxcnt < quota); - - ++cfv->stats.rx_napi_resched; - goto out; - -exit: - switch (err) { - case 0: - ++cfv->stats.rx_napi_complete; - - /* Really out of packets? (stolen from virtio_net)*/ - napi_complete(napi); - if (unlikely(!vringh_notify_enable_kern(cfv->vr_rx)) && - napi_schedule_prep(napi)) { - vringh_notify_disable_kern(cfv->vr_rx); - __napi_schedule(napi); - } - break; - - case -ENOMEM: - ++cfv->stats.rx_nomem; - dev_kfree_skb(skb); - /* Stop NAPI poll on OOM, we hope to be polled later */ - napi_complete(napi); - vringh_notify_enable_kern(cfv->vr_rx); - break; - - default: - /* We're doomed, any modem fault is fatal */ - netdev_warn(cfv->ndev, "Bad ring, disable device\n"); - cfv->ndev->stats.rx_dropped = riov->used - riov->i; - napi_complete(napi); - vringh_notify_disable_kern(cfv->vr_rx); - netif_carrier_off(cfv->ndev); - break; - } -out: - if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0) - vringh_notify(cfv->vr_rx); - return rxcnt; -} - -static void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx) -{ - struct cfv_info *cfv = vdev->priv; - - ++cfv->stats.rx_kicks; - vringh_notify_disable_kern(cfv->vr_rx); - napi_schedule(&cfv->napi); -} - -static void cfv_destroy_genpool(struct cfv_info *cfv) -{ - if (cfv->alloc_addr) - dma_free_coherent(cfv->vdev->dev.parent->parent, - cfv->allocsz, cfv->alloc_addr, - cfv->alloc_dma); - - if (!cfv->genpool) - return; - gen_pool_free(cfv->genpool, cfv->reserved_mem, - cfv->reserved_size); - gen_pool_destroy(cfv->genpool); - cfv->genpool = NULL; -} - -static int cfv_create_genpool(struct cfv_info *cfv) -{ - int err; - - /* dma_alloc can only allocate whole pages, and we need a more - * fine graned allocation so we use genpool. We ask for space needed - * by IP and a full ring. If the dma allcoation fails we retry with a - * smaller allocation size. - */ - err = -ENOMEM; - cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) * - (ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10; - if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu) - return -EINVAL; - - for (;;) { - if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) { - netdev_info(cfv->ndev, "Not enough device memory\n"); - return -ENOMEM; - } - - cfv->alloc_addr = dma_alloc_coherent( - cfv->vdev->dev.parent->parent, - cfv->allocsz, &cfv->alloc_dma, - GFP_ATOMIC); - if (cfv->alloc_addr) - break; - - cfv->allocsz = (cfv->allocsz * 3) >> 2; - } - - netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n", - cfv->allocsz); - - /* Allocate on 128 bytes boundaries (1 << 7)*/ - cfv->genpool = gen_pool_create(7, -1); - if (!cfv->genpool) - goto err; - - err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr, - (phys_addr_t)virt_to_phys(cfv->alloc_addr), - cfv->allocsz, -1); - if (err) - goto err; - - /* Reserve some memory for low memory situations. If we hit the roof - * in the memory pool, we stop TX flow and release the reserve. - */ - cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu; - cfv->reserved_mem = gen_pool_alloc(cfv->genpool, - cfv->reserved_size); - if (!cfv->reserved_mem) { - err = -ENOMEM; - goto err; - } - - cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx); - return 0; -err: - cfv_destroy_genpool(cfv); - return err; -} - -/* Enable the CAIF interface and allocate the memory-pool */ -static int cfv_netdev_open(struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - - if (cfv_create_genpool(cfv)) - return -ENOMEM; - - netif_carrier_on(netdev); - napi_enable(&cfv->napi); - - /* Schedule NAPI to read any pending packets */ - napi_schedule(&cfv->napi); - return 0; -} - -/* Disable the CAIF interface and free the memory-pool */ -static int cfv_netdev_close(struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - unsigned long flags; - struct buf_info *buf_info; - - /* Disable interrupts, queues and NAPI polling */ - netif_carrier_off(netdev); - virtqueue_disable_cb(cfv->vq_tx); - vringh_notify_disable_kern(cfv->vr_rx); - napi_disable(&cfv->napi); - - /* Release any TX buffers on both used and available rings */ - cfv_release_used_buf(cfv->vq_tx); - spin_lock_irqsave(&cfv->tx_lock, flags); - while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx))) - free_buf_info(cfv, buf_info); - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* Release all dma allocated memory and destroy the pool */ - cfv_destroy_genpool(cfv); - return 0; -} - -/* Allocate a buffer in dma-memory and copy skb to it */ -static struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv, - struct sk_buff *skb, - struct scatterlist *sg) -{ - struct caif_payload_info *info = (void *)&skb->cb; - struct buf_info *buf_info = NULL; - u8 pad_len, hdr_ofs; - - if (!cfv->genpool) - goto err; - - if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) { - netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n", - cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu); - goto err; - } - - buf_info = kmalloc_obj(struct buf_info, GFP_ATOMIC); - if (unlikely(!buf_info)) - goto err; - - /* Make the IP header aligned in the buffer */ - hdr_ofs = cfv->tx_hr + info->hdr_len; - pad_len = hdr_ofs & (IP_HDR_ALIGN - 1); - buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len; - - /* allocate dma memory buffer */ - buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size); - if (unlikely(!buf_info->vaddr)) - goto err; - - /* copy skbuf contents to send buffer */ - skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len); - sg_init_one(sg, buf_info->vaddr + pad_len, - skb->len + cfv->tx_hr + cfv->rx_hr); - - return buf_info; -err: - kfree(buf_info); - return NULL; -} - -/* Put the CAIF packet on the virtio ring and kick the receiver */ -static netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev) -{ - struct cfv_info *cfv = netdev_priv(netdev); - struct buf_info *buf_info; - struct scatterlist sg; - unsigned long flags; - bool flow_off = false; - int ret; - - /* garbage collect released buffers */ - cfv_release_used_buf(cfv->vq_tx); - spin_lock_irqsave(&cfv->tx_lock, flags); - - /* Flow-off check takes into account number of cpus to make sure - * virtqueue will not be overfilled in any possible smp conditions. - * - * Flow-on is triggered when sufficient buffers are freed - */ - if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) { - flow_off = true; - cfv->stats.tx_full_ring++; - } - - /* If we run out of memory, we release the memory reserve and retry - * allocation. - */ - buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); - if (unlikely(!buf_info)) { - cfv->stats.tx_no_mem++; - flow_off = true; - - if (cfv->reserved_mem && cfv->genpool) { - gen_pool_free(cfv->genpool, cfv->reserved_mem, - cfv->reserved_size); - cfv->reserved_mem = 0; - buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); - } - } - - if (unlikely(flow_off)) { - /* Turn flow on when a 1/4 of the descriptors are released */ - cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4; - /* Enable notifications of recycled TX buffers */ - virtqueue_enable_cb(cfv->vq_tx); - netif_tx_stop_all_queues(netdev); - } - - if (unlikely(!buf_info)) { - /* If the memory reserve does it's job, this shouldn't happen */ - netdev_warn(cfv->ndev, "Out of gen_pool memory\n"); - goto err; - } - - ret = virtqueue_add_outbuf(cfv->vq_tx, &sg, 1, buf_info, GFP_ATOMIC); - if (unlikely((ret < 0))) { - /* If flow control works, this shouldn't happen */ - netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n", - ret); - goto err; - } - - /* update netdev statistics */ - cfv->ndev->stats.tx_packets++; - cfv->ndev->stats.tx_bytes += skb->len; - spin_unlock_irqrestore(&cfv->tx_lock, flags); - - /* tell the remote processor it has a pending message to read */ - virtqueue_kick(cfv->vq_tx); - - dev_kfree_skb(skb); - return NETDEV_TX_OK; -err: - spin_unlock_irqrestore(&cfv->tx_lock, flags); - cfv->ndev->stats.tx_dropped++; - free_buf_info(cfv, buf_info); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -static void cfv_tx_release_tasklet(struct tasklet_struct *t) -{ - struct cfv_info *cfv = from_tasklet(cfv, t, tx_release_tasklet); - cfv_release_used_buf(cfv->vq_tx); -} - -static const struct net_device_ops cfv_netdev_ops = { - .ndo_open = cfv_netdev_open, - .ndo_stop = cfv_netdev_close, - .ndo_start_xmit = cfv_netdev_tx, -}; - -static void cfv_netdev_setup(struct net_device *netdev) -{ - netdev->netdev_ops = &cfv_netdev_ops; - netdev->type = ARPHRD_CAIF; - netdev->tx_queue_len = 100; - netdev->flags = IFF_POINTOPOINT | IFF_NOARP; - netdev->mtu = CFV_DEF_MTU_SIZE; - netdev->needs_free_netdev = true; -} - -/* Create debugfs counters for the device */ -static inline void debugfs_init(struct cfv_info *cfv) -{ - cfv->debugfs = debugfs_create_dir(netdev_name(cfv->ndev), NULL); - - debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs, - &cfv->stats.rx_napi_complete); - debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs, - &cfv->stats.rx_napi_resched); - debugfs_create_u32("rx-nomem", 0400, cfv->debugfs, - &cfv->stats.rx_nomem); - debugfs_create_u32("rx-kicks", 0400, cfv->debugfs, - &cfv->stats.rx_kicks); - debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs, - &cfv->stats.tx_full_ring); - debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs, - &cfv->stats.tx_no_mem); - debugfs_create_u32("tx-kicks", 0400, cfv->debugfs, - &cfv->stats.tx_kicks); - debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs, - &cfv->stats.tx_flow_on); -} - -/* Setup CAIF for the a virtio device */ -static int cfv_probe(struct virtio_device *vdev) -{ - vrh_callback_t *vrh_cbs = cfv_recv; - const char *cfv_netdev_name = "cfvrt"; - struct net_device *netdev; - struct cfv_info *cfv; - int err; - - netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name, - NET_NAME_UNKNOWN, cfv_netdev_setup); - if (!netdev) - return -ENOMEM; - - cfv = netdev_priv(netdev); - cfv->vdev = vdev; - cfv->ndev = netdev; - - spin_lock_init(&cfv->tx_lock); - - /* Get the RX virtio ring. This is a "host side vring". */ - err = -ENODEV; - if (!vdev->vringh_config || !vdev->vringh_config->find_vrhs) - goto err; - - err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs); - if (err) - goto err; - - /* Get the TX virtio ring. This is a "guest side vring". */ - cfv->vq_tx = virtio_find_single_vq(vdev, cfv_release_cb, "output"); - if (IS_ERR(cfv->vq_tx)) { - err = PTR_ERR(cfv->vq_tx); - goto err; - } - - /* Get the CAIF configuration from virtio config space, if available */ - if (vdev->config->get) { - virtio_cread(vdev, struct virtio_caif_transf_config, headroom, - &cfv->tx_hr); - virtio_cread(vdev, struct virtio_caif_transf_config, headroom, - &cfv->rx_hr); - virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, - &cfv->tx_tr); - virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, - &cfv->rx_tr); - virtio_cread(vdev, struct virtio_caif_transf_config, mtu, - &cfv->mtu); - virtio_cread(vdev, struct virtio_caif_transf_config, mtu, - &cfv->mru); - } else { - cfv->tx_hr = CFV_DEF_HEADROOM; - cfv->rx_hr = CFV_DEF_HEADROOM; - cfv->tx_tr = CFV_DEF_TAILROOM; - cfv->rx_tr = CFV_DEF_TAILROOM; - cfv->mtu = CFV_DEF_MTU_SIZE; - cfv->mru = CFV_DEF_MTU_SIZE; - } - - netdev->needed_headroom = cfv->tx_hr; - netdev->needed_tailroom = cfv->tx_tr; - - /* Disable buffer release interrupts unless we have stopped TX queues */ - virtqueue_disable_cb(cfv->vq_tx); - - netdev->mtu = cfv->mtu - cfv->tx_tr; - vdev->priv = cfv; - - /* Initialize NAPI poll context data */ - vringh_kiov_init(&cfv->ctx.riov, NULL, 0); - cfv->ctx.head = USHRT_MAX; - netif_napi_add_weight(netdev, &cfv->napi, cfv_rx_poll, - CFV_DEFAULT_QUOTA); - - tasklet_setup(&cfv->tx_release_tasklet, cfv_tx_release_tasklet); - - /* Carrier is off until netdevice is opened */ - netif_carrier_off(netdev); - - /* serialize netdev register + virtio_device_ready() with ndo_open() */ - rtnl_lock(); - - /* register Netdev */ - err = register_netdevice(netdev); - if (err) { - rtnl_unlock(); - dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err); - goto err; - } - - virtio_device_ready(vdev); - - rtnl_unlock(); - - debugfs_init(cfv); - - return 0; -err: - netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err); - - if (cfv->vr_rx) - vdev->vringh_config->del_vrhs(cfv->vdev); - if (cfv->vq_tx) - vdev->config->del_vqs(cfv->vdev); - free_netdev(netdev); - return err; -} - -static void cfv_remove(struct virtio_device *vdev) -{ - struct cfv_info *cfv = vdev->priv; - - rtnl_lock(); - dev_close(cfv->ndev); - rtnl_unlock(); - - tasklet_kill(&cfv->tx_release_tasklet); - debugfs_remove_recursive(cfv->debugfs); - - vringh_kiov_cleanup(&cfv->ctx.riov); - virtio_reset_device(vdev); - vdev->vringh_config->del_vrhs(cfv->vdev); - cfv->vr_rx = NULL; - vdev->config->del_vqs(cfv->vdev); - unregister_netdev(cfv->ndev); -} - -static struct virtio_device_id id_table[] = { - { VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID }, - { 0 }, -}; - -static unsigned int features[] = { -}; - -static struct virtio_driver caif_virtio_driver = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), - .driver.name = KBUILD_MODNAME, - .id_table = id_table, - .probe = cfv_probe, - .remove = cfv_remove, -}; - -module_virtio_driver(caif_virtio_driver); -MODULE_DEVICE_TABLE(virtio, id_table); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index b9423389c2ef..44d670904ad8 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -25,6 +25,9 @@ #include "mt7530.h" +#define MT7530_STATS_POLL_INTERVAL (1 * HZ) +#define MT7530_STATS_RATE_LIMIT (HZ / 10) + static struct mt753x_pcs *pcs_to_mt753x_pcs(struct phylink_pcs *pcs) { return container_of(pcs, struct mt753x_pcs, pcs); @@ -906,10 +909,9 @@ static void mt7530_get_rmon_stats(struct dsa_switch *ds, int port, *ranges = mt7530_rmon_ranges; } -static void mt7530_get_stats64(struct dsa_switch *ds, int port, - struct rtnl_link_stats64 *storage) +static void mt7530_read_port_stats64(struct mt7530_priv *priv, int port, + struct rtnl_link_stats64 *storage) { - struct mt7530_priv *priv = ds->priv; uint64_t data; /* MIB counter doesn't provide a FramesTransmittedOK but instead @@ -951,6 +953,54 @@ static void mt7530_get_stats64(struct dsa_switch *ds, int port, &storage->rx_crc_errors); } +static void mt7530_stats_refresh(struct mt7530_priv *priv) +{ + struct rtnl_link_stats64 stats = {}; + struct dsa_port *dp; + int port; + + dsa_switch_for_each_user_port(dp, priv->ds) { + port = dp->index; + + mt7530_read_port_stats64(priv, port, &stats); + + spin_lock_bh(&priv->stats_lock); + priv->ports[port].stats = stats; + priv->stats_last = jiffies; + spin_unlock_bh(&priv->stats_lock); + } +} + +static void mt7530_stats_poll(struct work_struct *work) +{ + struct mt7530_priv *priv = container_of(work, struct mt7530_priv, + stats_work.work); + + mt7530_stats_refresh(priv); + schedule_delayed_work(&priv->stats_work, + MT7530_STATS_POLL_INTERVAL); +} + +static void mt7530_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *storage) +{ + struct mt7530_priv *priv = ds->priv; + bool refresh; + + if (priv->bus) { + spin_lock_bh(&priv->stats_lock); + *storage = priv->ports[port].stats; + refresh = time_after(jiffies, priv->stats_last + + MT7530_STATS_RATE_LIMIT); + spin_unlock_bh(&priv->stats_lock); + if (refresh) + mod_delayed_work(system_percpu_wq, + &priv->stats_work, 0); + } else { + mt7530_read_port_stats64(priv, port, storage); + } +} + static void mt7530_get_eth_ctrl_stats(struct dsa_switch *ds, int port, struct ethtool_eth_ctrl_stats *ctrl_stats) { @@ -3137,9 +3187,24 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq_domain) mt7530_free_mdio_irq(priv); + if (!ret && priv->bus) { + mt7530_stats_refresh(priv); + schedule_delayed_work(&priv->stats_work, + MT7530_STATS_POLL_INTERVAL); + } + return ret; } +static void +mt753x_teardown(struct dsa_switch *ds) +{ + struct mt7530_priv *priv = ds->priv; + + if (priv->bus) + cancel_delayed_work_sync(&priv->stats_work); +} + static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) { @@ -3257,6 +3322,7 @@ static int mt7988_setup(struct dsa_switch *ds) static const struct dsa_switch_ops mt7530_switch_ops = { .get_tag_protocol = mtk_get_tag_protocol, .setup = mt753x_setup, + .teardown = mt753x_teardown, .preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port, .get_strings = mt7530_get_strings, .get_ethtool_stats = mt7530_get_ethtool_stats, @@ -3395,6 +3461,9 @@ mt7530_probe_common(struct mt7530_priv *priv) priv->ds->ops = &mt7530_switch_ops; priv->ds->phylink_mac_ops = &mt753x_phylink_mac_ops; mutex_init(&priv->reg_mutex); + spin_lock_init(&priv->stats_lock); + INIT_DELAYED_WORK(&priv->stats_work, mt7530_stats_poll); + dev_set_drvdata(dev, priv); return 0; diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 3e0090bed298..dd33b0df3419 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -796,6 +796,7 @@ struct mt7530_fdb { * @pvid: The VLAN specified is to be considered a PVID at ingress. Any * untagged frames will be assigned to the related VLAN. * @sgmii_pcs: Pointer to PCS instance for SerDes ports + * @stats: Cached port statistics for MDIO-connected switches */ struct mt7530_port { bool enable; @@ -803,6 +804,7 @@ struct mt7530_port { u32 pm; u16 pvid; struct phylink_pcs *sgmii_pcs; + struct rtnl_link_stats64 stats; }; /* Port 5 mode definitions of the MT7530 switch */ @@ -875,6 +877,9 @@ struct mt753x_info { * @create_sgmii: Pointer to function creating SGMII PCS instance(s) * @active_cpu_ports: Holding the active CPU ports * @mdiodev: The pointer to the MDIO device structure + * @stats_lock: Protects cached per-port stats from concurrent access + * @stats_work: Delayed work for polling MIB counters on MDIO switches + * @stats_last: Jiffies timestamp of last MIB counter poll */ struct mt7530_priv { struct device *dev; @@ -900,6 +905,9 @@ struct mt7530_priv { int (*create_sgmii)(struct mt7530_priv *priv); u8 active_cpu_ports; struct mdio_device *mdiodev; + spinlock_t stats_lock; /* protects cached stats counters */ + struct delayed_work stats_work; + unsigned long stats_last; }; struct mt7530_hw_vlan_entry { diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 31fa94dac627..c35cef01ec26 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -216,7 +216,7 @@ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ 0x0) #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ - (0xF << (((_extint) % 2))) + (0xF << (((_extint) % 2) * 4)) #define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ (((_extint) % 2) * 4) diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index d6bdad4baadd..f8a4eb365c3d 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -47,7 +47,9 @@ static int numdummies = 1; /* fake multicast ability */ -static void set_multicast_list(struct net_device *dev) +static void set_multicast_list(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { } @@ -87,7 +89,7 @@ static const struct net_device_ops dummy_netdev_ops = { .ndo_init = dummy_dev_init, .ndo_start_xmit = dummy_xmit, .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = set_multicast_list, + .ndo_set_rx_mode_async = set_multicast_list, .ndo_set_mac_address = eth_mac_addr, .ndo_get_stats64 = dummy_get_stats64, .ndo_change_carrier = dummy_change_carrier, diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c deleted file mode 100644 index fb68339e1511..000000000000 --- a/drivers/net/ethernet/3com/3c509.c +++ /dev/null @@ -1,1448 +0,0 @@ -/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ -/* - Written 1993-2000 by Donald Becker. - - Copyright 1994-2000 by Donald Becker. - Copyright 1993 United States Government as represented by the - Director, National Security Agency. This software may be used and - distributed according to the terms of the GNU General Public License, - incorporated herein by reference. - - This driver is for the 3Com EtherLinkIII series. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Known limitations: - Because of the way 3c509 ISA detection works it's difficult to predict - a priori which of several ISA-mode cards will be detected first. - - This driver does not use predictive interrupt mode, resulting in higher - packet latency but lower overhead. If interrupts are disabled for an - unusually long time it could also result in missed packets, but in - practice this rarely happens. - - - FIXES: - Alan Cox: Removed the 'Unexpected interrupt' bug. - Michael Meskes: Upgraded to Donald Becker's version 1.07. - Alan Cox: Increased the eeprom delay. Regardless of - what the docs say some people definitely - get problems with lower (but in card spec) - delays - v1.10 4/21/97 Fixed module code so that multiple cards may be detected, - other cleanups. -djb - Andrea Arcangeli: Upgraded to Donald Becker's version 1.12. - Rick Payne: Fixed SMP race condition - v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb - v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb - v1.15 1/31/98 Faster recovery for Tx errors. -djb - v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb - v1.18 12Mar2001 Andrew Morton - - Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz) - - Reviewed against 1.18 from scyld.com - v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com> - - ethtool support - v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com> - - Power Management support - v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com> - - Full duplex support - v1.19 16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca> - - Additional ethtool features - v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com> - - Increase *read_eeprom udelay to workaround oops with 2 cards. - v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org> - - Introduce driver model for EISA cards. - v1.20 04Feb2008 Ondrej Zary <linux@rainbow-software.org> - - convert to isa_driver and pnp_driver and some cleanups -*/ - -#define DRV_NAME "3c509" - -/* A few values that may be tweaked. */ - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (400*HZ/1000) - -#include <linux/module.h> -#include <linux/isa.h> -#include <linux/pnp.h> -#include <linux/string.h> -#include <linux/interrupt.h> -#include <linux/errno.h> -#include <linux/in.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/pm.h> -#include <linux/skbuff.h> -#include <linux/delay.h> /* for udelay() */ -#include <linux/spinlock.h> -#include <linux/ethtool.h> -#include <linux/device.h> -#include <linux/eisa.h> -#include <linux/bitops.h> - -#include <linux/uaccess.h> -#include <asm/io.h> -#include <asm/irq.h> - -#ifdef EL3_DEBUG -static int el3_debug = EL3_DEBUG; -#else -static int el3_debug = 2; -#endif - -/* Used to do a global count of all the cards in the system. Must be - * a global variable so that the eisa probe routines can increment - * it */ -static int el3_cards = 0; -#define EL3_MAX_CARDS 8 - -/* To minimize the size of the driver source I only define operating - constants if they are used several times. You'll need the manual - anyway if you want to understand driver details. */ -/* Offsets from base I/O address. */ -#define EL3_DATA 0x00 -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e -#define EEPROM_READ 0x80 - -#define EL3_IO_EXTENT 16 - -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) - - -/* The top five bits written to EL3_CMD are a command, the lower - 11 bits are the parameter, if applicable. */ -enum c509cmd { - TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, - RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, - TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, - FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, - SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, - SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, - StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11, - PowerDown = 28<<11, PowerAuto = 29<<11}; - -enum c509status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, - TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, }; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 }; - -/* Register window 1 offsets, the window used in normal operation. */ -#define TX_FIFO 0x00 -#define RX_FIFO 0x00 -#define RX_STATUS 0x08 -#define TX_STATUS 0x0B -#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ - -#define WN0_CONF_CTRL 0x04 /* Window 0: Configuration control register */ -#define WN0_ADDR_CONF 0x06 /* Window 0: Address configuration register */ -#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ -#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ -#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ -#define WN4_NETDIAG 0x06 /* Window 4: Net diagnostic */ -#define FD_ENABLE 0x8000 /* Enable full-duplex ("external loopback") */ - -/* - * Must be a power of two (we use a binary and in the - * circular queue) - */ -#define SKB_QUEUE_SIZE 64 - -enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA }; - -struct el3_private { - spinlock_t lock; - /* skb send-queue */ - int head, size; - struct sk_buff *queue[SKB_QUEUE_SIZE]; - enum el3_cardtype type; -}; -static int id_port; -static int current_tag; -static struct net_device *el3_devs[EL3_MAX_CARDS]; - -/* Parameters that may be passed into the module. */ -static int debug = -1; -static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 10; -#ifdef CONFIG_PNP -static int nopnp; -#endif - -static int el3_common_init(struct net_device *dev); -static void el3_common_remove(struct net_device *dev); -static ushort id_read_eeprom(int index); -static ushort read_eeprom(int ioaddr, int index); -static int el3_open(struct net_device *dev); -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t el3_interrupt(int irq, void *dev_id); -static void update_stats(struct net_device *dev); -static struct net_device_stats *el3_get_stats(struct net_device *dev); -static int el3_rx(struct net_device *dev); -static int el3_close(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void el3_tx_timeout (struct net_device *dev, unsigned int txqueue); -static void el3_down(struct net_device *dev); -static void el3_up(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; -#ifdef CONFIG_PM -static int el3_suspend(struct device *, pm_message_t); -static int el3_resume(struct device *); -#else -#define el3_suspend NULL -#define el3_resume NULL -#endif - - -/* generic device remove for all device types */ -static int el3_device_remove (struct device *device); -#ifdef CONFIG_NET_POLL_CONTROLLER -static void el3_poll_controller(struct net_device *dev); -#endif - -/* Return 0 on success, 1 on error, 2 when found already detected PnP card */ -static int el3_isa_id_sequence(__be16 *phys_addr) -{ - short lrs_state = 0xff; - int i; - - /* ISA boards are detected by sending the ID sequence to the - ID_PORT. We find cards past the first by setting the 'current_tag' - on cards as they are found. Cards with their tag set will not - respond to subsequent ID sequences. */ - - outb(0x00, id_port); - outb(0x00, id_port); - for (i = 0; i < 255; i++) { - outb(lrs_state, id_port); - lrs_state <<= 1; - lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state; - } - /* For the first probe, clear all board's tag registers. */ - if (current_tag == 0) - outb(0xd0, id_port); - else /* Otherwise kill off already-found boards. */ - outb(0xd8, id_port); - if (id_read_eeprom(7) != 0x6d50) - return 1; - /* Read in EEPROM data, which does contention-select. - Only the lowest address board will stay "on-line". - 3Com got the byte order backwards. */ - for (i = 0; i < 3; i++) - phys_addr[i] = htons(id_read_eeprom(i)); -#ifdef CONFIG_PNP - if (!nopnp) { - /* The ISA PnP 3c509 cards respond to the ID sequence too. - This check is needed in order not to register them twice. */ - for (i = 0; i < el3_cards; i++) { - struct el3_private *lp = netdev_priv(el3_devs[i]); - if (lp->type == EL3_PNP && - ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) { - if (el3_debug > 3) - pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n", - phys_addr[0] & 0xff, phys_addr[0] >> 8, - phys_addr[1] & 0xff, phys_addr[1] >> 8, - phys_addr[2] & 0xff, phys_addr[2] >> 8); - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - return 2; - } - } - } -#endif /* CONFIG_PNP */ - return 0; - -} - -static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr, - int irq, int if_port, enum el3_cardtype type) -{ - struct el3_private *lp = netdev_priv(dev); - - eth_hw_addr_set(dev, (u8 *)phys_addr); - dev->base_addr = ioaddr; - dev->irq = irq; - dev->if_port = if_port; - lp->type = type; -} - -static int el3_isa_match(struct device *pdev, unsigned int ndev) -{ - struct net_device *dev; - int ioaddr, isa_irq, if_port, err; - unsigned int iobase; - __be16 phys_addr[3]; - - while ((err = el3_isa_id_sequence(phys_addr)) == 2) - ; /* Skip to next card when PnP card found */ - if (err == 1) - return 0; - - iobase = id_read_eeprom(8); - if_port = iobase >> 14; - ioaddr = 0x200 + ((iobase & 0x1f) << 4); - if (irq[el3_cards] > 1 && irq[el3_cards] < 16) - isa_irq = irq[el3_cards]; - else - isa_irq = id_read_eeprom(9) >> 12; - - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) - return -ENOMEM; - - SET_NETDEV_DEV(dev, pdev); - - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) { - free_netdev(dev); - return 0; - } - - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - - /* Activate the adaptor at the EEPROM location. */ - outb((ioaddr >> 4) | 0xe0, id_port); - - EL3WINDOW(0); - if (inw(ioaddr) != 0x6d50) { - free_netdev(dev); - return 0; - } - - /* Free the interrupt so that some other card can use it. */ - outw(0x0f00, ioaddr + WN0_IRQ); - - el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA); - dev_set_drvdata(pdev, dev); - if (el3_common_init(dev)) { - free_netdev(dev); - return 0; - } - - el3_devs[el3_cards++] = dev; - return 1; -} - -static void el3_isa_remove(struct device *pdev, - unsigned int ndev) -{ - el3_device_remove(pdev); - dev_set_drvdata(pdev, NULL); -} - -#ifdef CONFIG_PM -static int el3_isa_suspend(struct device *dev, unsigned int n, - pm_message_t state) -{ - current_tag = 0; - return el3_suspend(dev, state); -} - -static int el3_isa_resume(struct device *dev, unsigned int n) -{ - struct net_device *ndev = dev_get_drvdata(dev); - int ioaddr = ndev->base_addr, err; - __be16 phys_addr[3]; - - while ((err = el3_isa_id_sequence(phys_addr)) == 2) - ; /* Skip to next card when PnP card found */ - if (err == 1) - return 0; - /* Set the adaptor tag so that the next card can be found. */ - outb(0xd0 + ++current_tag, id_port); - /* Enable the card */ - outb((ioaddr >> 4) | 0xe0, id_port); - EL3WINDOW(0); - if (inw(ioaddr) != 0x6d50) - return 1; - /* Free the interrupt so that some other card can use it. */ - outw(0x0f00, ioaddr + WN0_IRQ); - return el3_resume(dev); -} -#endif - -static struct isa_driver el3_isa_driver = { - .match = el3_isa_match, - .remove = el3_isa_remove, -#ifdef CONFIG_PM - .suspend = el3_isa_suspend, - .resume = el3_isa_resume, -#endif - .driver = { - .name = "3c509" - }, -}; -static int isa_registered; - -#ifdef CONFIG_PNP -static const struct pnp_device_id el3_pnp_ids[] = { - { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */ - { .id = "TCM5091" }, /* 3Com Etherlink III */ - { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */ - { .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */ - { .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */ - { .id = "PNP80f7" }, /* 3Com Etherlink III compatible */ - { .id = "PNP80f8" }, /* 3Com Etherlink III compatible */ - { .id = "" } -}; -MODULE_DEVICE_TABLE(pnp, el3_pnp_ids); - -static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id) -{ - short i; - int ioaddr, irq, if_port; - __be16 phys_addr[3]; - struct net_device *dev = NULL; - int err; - - ioaddr = pnp_port_start(pdev, 0); - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp")) - return -EBUSY; - irq = pnp_irq(pdev, 0); - EL3WINDOW(0); - for (i = 0; i < 3; i++) - phys_addr[i] = htons(read_eeprom(ioaddr, i)); - if_port = read_eeprom(ioaddr, 8) >> 14; - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) { - release_region(ioaddr, EL3_IO_EXTENT); - return -ENOMEM; - } - SET_NETDEV_DEV(dev, &pdev->dev); - - el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP); - pnp_set_drvdata(pdev, dev); - err = el3_common_init(dev); - - if (err) { - pnp_set_drvdata(pdev, NULL); - free_netdev(dev); - return err; - } - - el3_devs[el3_cards++] = dev; - return 0; -} - -static void el3_pnp_remove(struct pnp_dev *pdev) -{ - el3_common_remove(pnp_get_drvdata(pdev)); - pnp_set_drvdata(pdev, NULL); -} - -#ifdef CONFIG_PM -static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state) -{ - return el3_suspend(&pdev->dev, state); -} - -static int el3_pnp_resume(struct pnp_dev *pdev) -{ - return el3_resume(&pdev->dev); -} -#endif - -static struct pnp_driver el3_pnp_driver = { - .name = "3c509", - .id_table = el3_pnp_ids, - .probe = el3_pnp_probe, - .remove = el3_pnp_remove, -#ifdef CONFIG_PM - .suspend = el3_pnp_suspend, - .resume = el3_pnp_resume, -#endif -}; -static int pnp_registered; -#endif /* CONFIG_PNP */ - -#ifdef CONFIG_EISA -static const struct eisa_device_id el3_eisa_ids[] = { - { "TCM5090" }, - { "TCM5091" }, - { "TCM5092" }, - { "TCM5093" }, - { "TCM5094" }, - { "TCM5095" }, - { "TCM5098" }, - { "" } -}; -MODULE_DEVICE_TABLE(eisa, el3_eisa_ids); - -static int el3_eisa_probe (struct device *device); - -static struct eisa_driver el3_eisa_driver = { - .id_table = el3_eisa_ids, - .driver = { - .name = "3c579", - .probe = el3_eisa_probe, - .remove = el3_device_remove, - .suspend = el3_suspend, - .resume = el3_resume, - } -}; -static int eisa_registered; -#endif - -static const struct net_device_ops netdev_ops = { - .ndo_open = el3_open, - .ndo_stop = el3_close, - .ndo_start_xmit = el3_start_xmit, - .ndo_get_stats = el3_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_tx_timeout = el3_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = el3_poll_controller, -#endif -}; - -static int el3_common_init(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - int err; - static const char * const if_names[] = { - "10baseT", "AUI", "undefined", "BNC" - }; - - spin_lock_init(&lp->lock); - - if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */ - dev->if_port = (dev->mem_start & 0x0f); - } else { /* xcvr codes 0/8 */ - /* use eeprom value, but save user's full-duplex selection */ - dev->if_port |= (dev->mem_start & 0x08); - } - - /* The EL3-specific entries in the device structure. */ - dev->netdev_ops = &netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - dev->ethtool_ops = ðtool_ops; - - err = register_netdev(dev); - if (err) { - pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n", - dev->base_addr, dev->irq); - release_region(dev->base_addr, EL3_IO_EXTENT); - return err; - } - - pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n", - dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)], - dev->dev_addr, dev->irq); - - return 0; - -} - -static void el3_common_remove (struct net_device *dev) -{ - unregister_netdev (dev); - release_region(dev->base_addr, EL3_IO_EXTENT); - free_netdev (dev); -} - -#ifdef CONFIG_EISA -static int el3_eisa_probe(struct device *device) -{ - short i; - int ioaddr, irq, if_port; - __be16 phys_addr[3]; - struct net_device *dev = NULL; - struct eisa_device *edev; - int err; - - /* Yeepee, The driver framework is calling us ! */ - edev = to_eisa_device (device); - ioaddr = edev->base_addr; - - if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa")) - return -EBUSY; - - /* Change the register set to the configuration window 0. */ - outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD); - - irq = inw(ioaddr + WN0_IRQ) >> 12; - if_port = inw(ioaddr + 6)>>14; - for (i = 0; i < 3; i++) - phys_addr[i] = htons(read_eeprom(ioaddr, i)); - - /* Restore the "Product ID" to the EEPROM read register. */ - read_eeprom(ioaddr, 3); - - dev = alloc_etherdev(sizeof (struct el3_private)); - if (dev == NULL) { - release_region(ioaddr, EL3_IO_EXTENT); - return -ENOMEM; - } - - SET_NETDEV_DEV(dev, device); - - el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA); - eisa_set_drvdata (edev, dev); - err = el3_common_init(dev); - - if (err) { - eisa_set_drvdata (edev, NULL); - free_netdev(dev); - return err; - } - - el3_devs[el3_cards++] = dev; - return 0; -} -#endif - -/* This remove works for all device types. - * - * The net dev must be stored in the driver data field */ -static int el3_device_remove(struct device *device) -{ - struct net_device *dev; - - dev = dev_get_drvdata(device); - - el3_common_remove (dev); - return 0; -} - -/* Read a word from the EEPROM using the regular EEPROM access register. - Assume that we are in register window zero. - */ -static ushort read_eeprom(int ioaddr, int index) -{ - outw(EEPROM_READ + index, ioaddr + 10); - /* Pause for at least 162 us. for the read to take place. - Some chips seem to require much longer */ - mdelay(2); - return inw(ioaddr + 12); -} - -/* Read a word from the EEPROM when in the ISA ID probe state. */ -static ushort id_read_eeprom(int index) -{ - int bit, word = 0; - - /* Issue read command, and pause for at least 162 us. for it to complete. - Assume extra-fast 16Mhz bus. */ - outb(EEPROM_READ + index, id_port); - - /* Pause for at least 162 us. for the read to take place. */ - /* Some chips seem to require much longer */ - mdelay(4); - - for (bit = 15; bit >= 0; bit--) - word = (word << 1) + (inb(id_port) & 0x01); - - if (el3_debug > 3) - pr_debug(" 3c509 EEPROM word %d %#4.4x.\n", index, word); - - return word; -} - - -static int -el3_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - int i; - - outw(TxReset, ioaddr + EL3_CMD); - outw(RxReset, ioaddr + EL3_CMD); - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - - i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev); - if (i) - return i; - - EL3WINDOW(0); - if (el3_debug > 3) - pr_debug("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name, - dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS)); - - el3_up(dev); - - if (el3_debug > 3) - pr_debug("%s: Opened 3c509 IRQ %d status %4.4x.\n", - dev->name, dev->irq, inw(ioaddr + EL3_STATUS)); - - return 0; -} - -static void -el3_tx_timeout (struct net_device *dev, unsigned int txqueue) -{ - int ioaddr = dev->base_addr; - - /* Transmitter timeout, serious problems. */ - pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n", - dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS), - inw(ioaddr + TX_FREE)); - dev->stats.tx_errors++; - netif_trans_update(dev); /* prevent tx timeout */ - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - outw(TxEnable, ioaddr + EL3_CMD); - netif_wake_queue(dev); -} - - -static netdev_tx_t -el3_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - unsigned long flags; - - netif_stop_queue (dev); - - dev->stats.tx_bytes += skb->len; - - if (el3_debug > 4) { - pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n", - dev->name, skb->len, inw(ioaddr + EL3_STATUS)); - } - /* - * We lock the driver against other processors. Note - * we don't need to lock versus the IRQ as we suspended - * that. This means that we lose the ability to take - * an RX during a TX upload. That sucks a bit with SMP - * on an original 3c509 (2K buffer) - * - * Using disable_irq stops us crapping on other - * time sensitive devices. - */ - - spin_lock_irqsave(&lp->lock, flags); - - /* Put out the doubleword header... */ - outw(skb->len, ioaddr + TX_FIFO); - outw(0x00, ioaddr + TX_FIFO); - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - - if (inw(ioaddr + TX_FREE) > 1536) - netif_start_queue(dev); - else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); - - spin_unlock_irqrestore(&lp->lock, flags); - - dev_consume_skb_any (skb); - - /* Clear the Tx status stack. */ - { - short tx_status; - int i = 4; - - while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) dev->stats.tx_aborted_errors++; - if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); - if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } - } - return NETDEV_TX_OK; -} - -/* The EL3 interrupt handler. */ -static irqreturn_t -el3_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct el3_private *lp; - int ioaddr, status; - int i = max_interrupt_work; - - lp = netdev_priv(dev); - spin_lock(&lp->lock); - - ioaddr = dev->base_addr; - - if (el3_debug > 4) { - status = inw(ioaddr + EL3_STATUS); - pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status); - } - - while ((status = inw(ioaddr + EL3_STATUS)) & - (IntLatch | RxComplete | StatsFull)) { - - if (status & RxComplete) - el3_rx(dev); - - if (status & TxAvailable) { - if (el3_debug > 5) - pr_debug(" TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue (dev); - } - if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) { - /* Handle all uncommon interrupts. */ - if (status & StatsFull) /* Empty statistics. */ - update_stats(dev); - if (status & RxEarly) { /* Rx early is unused. */ - el3_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & TxComplete) { /* Really Tx error. */ - short tx_status; - int i = 4; - - while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) { - if (tx_status & 0x38) dev->stats.tx_aborted_errors++; - if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD); - if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD); - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } - } - if (status & AdapterFailure) { - /* Adapter failure requires Rx reset and reinit. */ - outw(RxReset, ioaddr + EL3_CMD); - /* Set the Rx filter to the current state. */ - outw(SetRxFilter | RxStation | RxBroadcast - | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0) - | (dev->flags & IFF_PROMISC ? RxProm : 0), - ioaddr + EL3_CMD); - outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } - } - - if (--i < 0) { - pr_err("%s: Infinite loop in interrupt, status %4.4x.\n", - dev->name, status); - /* Clear all interrupts. */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */ - } - - if (el3_debug > 4) { - pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, - inw(ioaddr + EL3_STATUS)); - } - spin_unlock(&lp->lock); - return IRQ_HANDLED; -} - - -#ifdef CONFIG_NET_POLL_CONTROLLER -/* - * Polling receive - used by netconsole and other diagnostic tools - * to allow network i/o with interrupts disabled. - */ -static void el3_poll_controller(struct net_device *dev) -{ - disable_irq(dev->irq); - el3_interrupt(dev->irq, dev); - enable_irq(dev->irq); -} -#endif - -static struct net_device_stats * -el3_get_stats(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - - /* - * This is fast enough not to bother with disable IRQ - * stuff. - */ - - spin_lock_irqsave(&lp->lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->lock, flags); - return &dev->stats; -} - -/* Update statistics. We change to register window 6, so this should be run - single-threaded if the device is active. This is expected to be a rare - operation, and it's simpler for the rest of the driver to assume that - window 1 is always valid rather than use a special window-state variable. - */ -static void update_stats(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - - if (el3_debug > 5) - pr_debug(" Updating the statistics.\n"); - /* Turn off statistics updates while reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - /* Rx packets */ inb(ioaddr + 7); - /* Tx deferrals */ inb(ioaddr + 8); - inw(ioaddr + 10); /* Total Rx and Tx octets. */ - inw(ioaddr + 12); - - /* Back to window 1, and turn statistics back on. */ - EL3WINDOW(1); - outw(StatsEnable, ioaddr + EL3_CMD); -} - -static int -el3_rx(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - short rx_status; - - if (el3_debug > 5) - pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); - while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) { - if (rx_status & 0x4000) { /* Error, update stats. */ - short error = rx_status & 0x3800; - - outw(RxDiscard, ioaddr + EL3_CMD); - dev->stats.rx_errors++; - switch (error) { - case 0x0000: dev->stats.rx_over_errors++; break; - case 0x0800: dev->stats.rx_length_errors++; break; - case 0x1000: dev->stats.rx_frame_errors++; break; - case 0x1800: dev->stats.rx_length_errors++; break; - case 0x2000: dev->stats.rx_frame_errors++; break; - case 0x2800: dev->stats.rx_crc_errors++; break; - } - } else { - short pkt_len = rx_status & 0x7ff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5); - if (el3_debug > 4) - pr_debug("Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); /* Align IP on 16 byte */ - - /* 'skb->data' points to the start of sk_buff data area. */ - insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), - (pkt_len + 3) >> 2); - - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ - skb->protocol = eth_type_trans(skb,dev); - netif_rx(skb); - dev->stats.rx_bytes += pkt_len; - dev->stats.rx_packets++; - continue; - } - outw(RxDiscard, ioaddr + EL3_CMD); - dev->stats.rx_dropped++; - if (el3_debug) - pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", - dev->name, pkt_len); - } - inw(ioaddr + EL3_STATUS); /* Delay. */ - while (inw(ioaddr + EL3_STATUS) & 0x1000) - pr_debug(" Waiting for 3c509 to discard packet, status %x.\n", - inw(ioaddr + EL3_STATUS) ); - } - - return 0; -} - -/* - * Set or clear the multicast filter for this adaptor. - */ -static void -set_multicast_list(struct net_device *dev) -{ - unsigned long flags; - struct el3_private *lp = netdev_priv(dev); - int ioaddr = dev->base_addr; - int mc_count = netdev_mc_count(dev); - - if (el3_debug > 1) { - static int old; - if (old != mc_count) { - old = mc_count; - pr_debug("%s: Setting Rx mode to %d addresses.\n", - dev->name, mc_count); - } - } - spin_lock_irqsave(&lp->lock, flags); - if (dev->flags&IFF_PROMISC) { - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, - ioaddr + EL3_CMD); - } - else if (mc_count || (dev->flags&IFF_ALLMULTI)) { - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD); - } - else - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); - spin_unlock_irqrestore(&lp->lock, flags); -} - -static int -el3_close(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - struct el3_private *lp = netdev_priv(dev); - - if (el3_debug > 2) - pr_debug("%s: Shutting down ethercard.\n", dev->name); - - el3_down(dev); - - free_irq(dev->irq, dev); - /* Switching back to window 0 disables the IRQ. */ - EL3WINDOW(0); - if (lp->type != EL3_EISA) { - /* But we explicitly zero the IRQ line select anyway. Don't do - * it on EISA cards, it prevents the module from getting an - * IRQ after unload+reload... */ - outw(0x0f00, ioaddr + WN0_IRQ); - } - - return 0; -} - -static int -el3_link_ok(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - u16 tmp; - - EL3WINDOW(4); - tmp = inw(ioaddr + WN4_MEDIA); - EL3WINDOW(1); - return tmp & (1<<11); -} - -static void -el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd) -{ - u16 tmp; - int ioaddr = dev->base_addr; - u32 supported; - - EL3WINDOW(0); - /* obtain current transceiver via WN4_MEDIA? */ - tmp = inw(ioaddr + WN0_ADDR_CONF); - switch (tmp >> 14) { - case 0: - cmd->base.port = PORT_TP; - break; - case 1: - cmd->base.port = PORT_AUI; - break; - case 3: - cmd->base.port = PORT_BNC; - break; - default: - break; - } - - cmd->base.duplex = DUPLEX_HALF; - supported = 0; - tmp = inw(ioaddr + WN0_CONF_CTRL); - if (tmp & (1<<13)) - supported |= SUPPORTED_AUI; - if (tmp & (1<<12)) - supported |= SUPPORTED_BNC; - if (tmp & (1<<9)) { - supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full; /* hmm... */ - EL3WINDOW(4); - tmp = inw(ioaddr + WN4_NETDIAG); - if (tmp & FD_ENABLE) - cmd->base.duplex = DUPLEX_FULL; - } - - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - supported); - cmd->base.speed = SPEED_10; - EL3WINDOW(1); -} - -static int -el3_netdev_set_ecmd(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - u16 tmp; - int ioaddr = dev->base_addr; - - if (cmd->base.speed != SPEED_10) - return -EINVAL; - if ((cmd->base.duplex != DUPLEX_HALF) && - (cmd->base.duplex != DUPLEX_FULL)) - return -EINVAL; - - /* change XCVR type */ - EL3WINDOW(0); - tmp = inw(ioaddr + WN0_ADDR_CONF); - switch (cmd->base.port) { - case PORT_TP: - tmp &= ~(3<<14); - dev->if_port = 0; - break; - case PORT_AUI: - tmp |= (1<<14); - dev->if_port = 1; - break; - case PORT_BNC: - tmp |= (3<<14); - dev->if_port = 3; - break; - default: - return -EINVAL; - } - - outw(tmp, ioaddr + WN0_ADDR_CONF); - if (dev->if_port == 3) { - /* fire up the DC-DC convertor if BNC gets enabled */ - tmp = inw(ioaddr + WN0_ADDR_CONF); - if (tmp & (3 << 14)) { - outw(StartCoax, ioaddr + EL3_CMD); - udelay(800); - } else - return -EIO; - } - - EL3WINDOW(4); - tmp = inw(ioaddr + WN4_NETDIAG); - if (cmd->base.duplex == DUPLEX_FULL) - tmp |= FD_ENABLE; - else - tmp &= ~FD_ENABLE; - outw(tmp, ioaddr + WN4_NETDIAG); - EL3WINDOW(1); - - return 0; -} - -static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); -} - -static int el3_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct el3_private *lp = netdev_priv(dev); - - spin_lock_irq(&lp->lock); - el3_netdev_get_ecmd(dev, cmd); - spin_unlock_irq(&lp->lock); - return 0; -} - -static int el3_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct el3_private *lp = netdev_priv(dev); - int ret; - - spin_lock_irq(&lp->lock); - ret = el3_netdev_set_ecmd(dev, cmd); - spin_unlock_irq(&lp->lock); - return ret; -} - -static u32 el3_get_link(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - u32 ret; - - spin_lock_irq(&lp->lock); - ret = el3_link_ok(dev); - spin_unlock_irq(&lp->lock); - return ret; -} - -static u32 el3_get_msglevel(struct net_device *dev) -{ - return el3_debug; -} - -static void el3_set_msglevel(struct net_device *dev, u32 v) -{ - el3_debug = v; -} - -static const struct ethtool_ops ethtool_ops = { - .get_drvinfo = el3_get_drvinfo, - .get_link = el3_get_link, - .get_msglevel = el3_get_msglevel, - .set_msglevel = el3_set_msglevel, - .get_link_ksettings = el3_get_link_ksettings, - .set_link_ksettings = el3_set_link_ksettings, -}; - -static void -el3_down(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - - netif_stop_queue(dev); - - /* Turn off statistics ASAP. We update lp->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - if (dev->if_port == 3) - /* Turn off thinnet power. Green! */ - outw(StopCoax, ioaddr + EL3_CMD); - else if (dev->if_port == 0) { - /* Disable link beat and jabber, if_port may change here next open(). */ - EL3WINDOW(4); - outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA); - } - - outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); - - update_stats(dev); -} - -static void -el3_up(struct net_device *dev) -{ - int i, sw_info, net_diag; - int ioaddr = dev->base_addr; - - /* Activating the board required and does no harm otherwise */ - outw(0x0001, ioaddr + 4); - - /* Set the IRQ line. */ - outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ); - - /* Set the station address in window 2 each time opened. */ - EL3WINDOW(2); - - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - - if ((dev->if_port & 0x03) == 3) /* BNC interface */ - /* Start the thinnet transceiver. We should really wait 50ms...*/ - outw(StartCoax, ioaddr + EL3_CMD); - else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */ - /* Combine secondary sw_info word (the adapter level) and primary - sw_info word (duplex setting plus other useless bits) */ - EL3WINDOW(0); - sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) | - (read_eeprom(ioaddr, 0x0d) & 0xBff0); - - EL3WINDOW(4); - net_diag = inw(ioaddr + WN4_NETDIAG); - net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */ - pr_info("%s: ", dev->name); - switch (dev->if_port & 0x0c) { - case 12: - /* force full-duplex mode if 3c5x9b */ - if (sw_info & 0x000f) { - pr_cont("Forcing 3c5x9b full-duplex mode"); - break; - } - fallthrough; - case 8: - /* set full-duplex mode based on eeprom config setting */ - if ((sw_info & 0x000f) && (sw_info & 0x8000)) { - pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)"); - break; - } - fallthrough; - default: - /* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */ - pr_cont("Setting 3c5x9/3c5x9B half-duplex mode"); - net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */ - } - - outw(net_diag, ioaddr + WN4_NETDIAG); - pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info); - if (el3_debug > 3) - pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag); - /* Enable link beat and jabber check. */ - outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA); - } - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 9; i++) - inb(ioaddr + i); - inw(ioaddr + 10); - inw(ioaddr + 12); - - /* Switch to register set 1 for normal use. */ - EL3WINDOW(1); - - /* Accept b-case and phys addr only. */ - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull, - ioaddr + EL3_CMD); - - netif_start_queue(dev); -} - -/* Power Management support functions */ -#ifdef CONFIG_PM - -static int -el3_suspend(struct device *pdev, pm_message_t state) -{ - unsigned long flags; - struct net_device *dev; - struct el3_private *lp; - int ioaddr; - - dev = dev_get_drvdata(pdev); - lp = netdev_priv(dev); - ioaddr = dev->base_addr; - - spin_lock_irqsave(&lp->lock, flags); - - if (netif_running(dev)) - netif_device_detach(dev); - - el3_down(dev); - outw(PowerDown, ioaddr + EL3_CMD); - - spin_unlock_irqrestore(&lp->lock, flags); - return 0; -} - -static int -el3_resume(struct device *pdev) -{ - unsigned long flags; - struct net_device *dev; - struct el3_private *lp; - int ioaddr; - - dev = dev_get_drvdata(pdev); - lp = netdev_priv(dev); - ioaddr = dev->base_addr; - - spin_lock_irqsave(&lp->lock, flags); - - outw(PowerUp, ioaddr + EL3_CMD); - EL3WINDOW(0); - el3_up(dev); - - if (netif_running(dev)) - netif_device_attach(dev); - - spin_unlock_irqrestore(&lp->lock, flags); - return 0; -} - -#endif /* CONFIG_PM */ - -module_param(debug,int, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param(max_interrupt_work, int, 0); -MODULE_PARM_DESC(debug, "debug level (0-6)"); -MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); -MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt"); -#ifdef CONFIG_PNP -module_param(nopnp, int, 0); -MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)"); -#endif /* CONFIG_PNP */ -MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver"); -MODULE_LICENSE("GPL"); - -static int __init el3_init_module(void) -{ - int ret = 0; - - if (debug >= 0) - el3_debug = debug; - -#ifdef CONFIG_PNP - if (!nopnp) { - ret = pnp_register_driver(&el3_pnp_driver); - if (!ret) - pnp_registered = 1; - } -#endif - /* Select an open I/O location at 0x1*0 to do ISA contention select. */ - /* Start with 0x110 to avoid some sound cards.*/ - for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) { - if (!request_region(id_port, 1, "3c509-control")) - continue; - outb(0x00, id_port); - outb(0xff, id_port); - if (inb(id_port) & 0x01) - break; - else - release_region(id_port, 1); - } - if (id_port >= 0x200) { - id_port = 0; - pr_err("No I/O port available for 3c509 activation.\n"); - } else { - ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS); - if (!ret) - isa_registered = 1; - } -#ifdef CONFIG_EISA - ret = eisa_driver_register(&el3_eisa_driver); - if (!ret) - eisa_registered = 1; -#endif - -#ifdef CONFIG_PNP - if (pnp_registered) - ret = 0; -#endif - if (isa_registered) - ret = 0; -#ifdef CONFIG_EISA - if (eisa_registered) - ret = 0; -#endif - return ret; -} - -static void __exit el3_cleanup_module(void) -{ -#ifdef CONFIG_PNP - if (pnp_registered) - pnp_unregister_driver(&el3_pnp_driver); -#endif - if (isa_registered) - isa_unregister_driver(&el3_isa_driver); - if (id_port) - release_region(id_port, 1); -#ifdef CONFIG_EISA - if (eisa_registered) - eisa_driver_unregister(&el3_eisa_driver); -#endif -} - -module_init (el3_init_module); -module_exit (el3_cleanup_module); diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c deleted file mode 100644 index 2227c83a4862..000000000000 --- a/drivers/net/ethernet/3com/3c515.c +++ /dev/null @@ -1,1566 +0,0 @@ -/* - Written 1997-1998 by Donald Becker. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - - 2000/2/2- Added support for kernel-level ISAPnP - by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo - Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox. - - 2001/11/17 - Added ethtool support (jgarzik) - - 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk) - -*/ - -#define DRV_NAME "3c515" - -#define CORKSCREW 1 - -/* "Knobs" that adjust features and parameters. */ -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1512 effectively disables this feature. */ -static int rx_copybreak = 200; - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 20; - -/* Enable the automatic media selection code -- usually set. */ -#define AUTOMEDIA 1 - -/* Allow the use of fragment bus master transfers instead of only - programmed-I/O for Vortex cards. Full-bus-master transfers are always - enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, - the feature may be turned on using 'options'. */ -#define VORTEX_BUS_MASTER - -/* A few values that may be tweaked. */ -/* Keep the ring sizes a power of two for efficiency. */ -#define TX_RING_SIZE 16 -#define RX_RING_SIZE 16 -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ - -#include <linux/module.h> -#include <linux/isapnp.h> -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/in.h> -#include <linux/ioport.h> -#include <linux/skbuff.h> -#include <linux/etherdevice.h> -#include <linux/interrupt.h> -#include <linux/timer.h> -#include <linux/ethtool.h> -#include <linux/bitops.h> -#include <linux/uaccess.h> - -#include <net/Space.h> - -#include <asm/io.h> -#include <asm/dma.h> - -#define NEW_MULTICAST -#include <linux/delay.h> - -#define MAX_UNITS 8 - -MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); -MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver"); -MODULE_LICENSE("GPL"); - -/* "Knobs" for adjusting internal parameters. */ -/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ -#define DRIVER_DEBUG 1 -/* Some values here only for performance evaluation and path-coverage - debugging. */ -static int rx_nocopy, rx_copy, queued_packet; - -/* Number of times to check to see if the Tx FIFO has space, used in some - limited cases. */ -#define WAIT_TX_AVAIL 200 - -/* Operational parameter that usually are not changed. */ -#define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */ - -/* The size here is somewhat misleading: the Corkscrew also uses the ISA - aliased registers at <base>+0x400. - */ -#define CORKSCREW_TOTAL_SIZE 0x20 - -#ifdef DRIVER_DEBUG -static int corkscrew_debug = DRIVER_DEBUG; -#else -static int corkscrew_debug = 1; -#endif - -#define CORKSCREW_ID 10 - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL, -3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout, -it's not practical to integrate this driver with the other EtherLink drivers. - -II. Board-specific settings - -The Corkscrew has an EEPROM for configuration, but no special settings are -needed for Linux. - -III. Driver operation - -The 3c515 series use an interface that's very similar to the 3c900 "Boomerang" -PCI cards, with the bus master interface extensively modified to work with -the ISA bus. - -The card is capable of full-bus-master transfers with separate -lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, -DEC Tulip and Intel Speedo3. - -This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate -receive buffer. This scheme allocates full-sized skbuffs as receive -buffers. The value RX_COPYBREAK is used as the copying breakpoint: it is -chosen to trade-off the memory wasted by passing the full-sized skbuff to -the queue layer for all frames vs. the copying cost of copying a frame to a -correctly-sized skbuff. - - -IIIC. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the netif -layer. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -IV. Notes - -Thanks to Terry Murphy of 3Com for providing documentation and a development -board. - -The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com -project names. I use these names to eliminate confusion -- 3Com product -numbers and names are very similar and often confused. - -The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes! -This driver only supports ethernet frames because of the recent MTU limit -of 1.5K, but the changes to support 4.5K are minimal. -*/ - -/* Operational definitions. - These are not used by other compilation units and thus are not - exported in a ".h" file. - - First the windows. There are eight register windows, with the command - and status registers available in each. - */ -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e - -/* The top five bits written to EL3_CMD are a command, the lower - 11 bits are the parameter, if applicable. - Note that 11 parameters bits was fine for ethernet, but the new chips - can handle FDDI length frames (~4500 octets) and now parameters count - 32-bit 'Dwords' rather than octets. */ - -enum corkscrew_cmd { - TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11, - RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11, - UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2, - DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11, - TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11, - AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11, - SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11, - SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11, - StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11, - StatsDisable = 22 << 11, StopCoax = 23 << 11, -}; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 -}; - -/* Bits in the general status register. */ -enum corkscrew_status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, - TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, - DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10, - DMAInProgress = 1 << 11, /* DMA controller is still busy. */ - CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */ -}; - -/* Register window 1 offsets, the window used in normal operation. - On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */ -enum Window1 { - TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, - RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B, - TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ -}; -enum Window0 { - Wn0IRQ = 0x08, -#if defined(CORKSCREW) - Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */ - Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */ -#else - Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ - Wn0EepromData = 12, /* Window 0: EEPROM results register. */ -#endif -}; -enum Win0_EEPROM_bits { - EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, - EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ - EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ -}; - -/* EEPROM locations. */ -enum eeprom_offset { - PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3, - EtherLink3ID = 7, -}; - -enum Window3 { /* Window 3: MAC/config bits. */ - Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8, -}; -enum wn3_config { - Ram_size = 7, - Ram_width = 8, - Ram_speed = 0x30, - Rom_size = 0xc0, - Ram_split_shift = 16, - Ram_split = 3 << Ram_split_shift, - Xcvr_shift = 20, - Xcvr = 7 << Xcvr_shift, - Autoselect = 0x1000000, -}; - -enum Window4 { - Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */ -}; -enum Win4_Media_bits { - Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ - Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */ - Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */ - Media_LnkBeat = 0x0800, -}; -enum Window7 { /* Window 7: Bus Master control. */ - Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, -}; - -/* Boomerang-style bus master control registers. Note ISA aliases! */ -enum MasterCtrl { - PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen = - 0x40c, - TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418, -}; - -/* The Rx and Tx descriptor lists. - Caution Alpha hackers: these types are 32 bits! Note also the 8 byte - alignment contraint on tx_ring[] and rx_ring[]. */ -struct boom_rx_desc { - u32 next; - s32 status; - u32 addr; - s32 length; -}; - -/* Values for the Rx status entry. */ -enum rx_desc_status { - RxDComplete = 0x00008000, RxDError = 0x4000, - /* See boomerang_rx() for actual error bits */ -}; - -struct boom_tx_desc { - u32 next; - s32 status; - u32 addr; - s32 length; -}; - -struct corkscrew_private { - const char *product_name; - struct list_head list; - struct net_device *our_dev; - /* The Rx and Tx rings are here to keep them quad-word-aligned. */ - struct boom_rx_desc rx_ring[RX_RING_SIZE]; - struct boom_tx_desc tx_ring[TX_RING_SIZE]; - /* The addresses of transmit- and receive-in-place skbuffs. */ - struct sk_buff *rx_skbuff[RX_RING_SIZE]; - struct sk_buff *tx_skbuff[TX_RING_SIZE]; - unsigned int cur_rx, cur_tx; /* The next free ring entry */ - unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */ - struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ - struct timer_list timer; /* Media selection timer. */ - int capabilities ; /* Adapter capabilities word. */ - int options; /* User-settable misc. driver options. */ - int last_rx_packets; /* For media autoselection. */ - unsigned int available_media:8, /* From Wn3_Options */ - media_override:3, /* Passed-in media type. */ - default_media:3, /* Read from the EEPROM. */ - full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */ - full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */ - tx_full:1; - spinlock_t lock; - struct device *dev; -}; - -/* The action to take with a media selection timer tick. - Note that we deviate from the 3Com order by checking 10base2 before AUI. - */ -enum xcvr_types { - XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, - XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8, -}; - -static struct media_table { - char *name; - unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ - mask:8, /* The transceiver-present bit in Wn3_Config. */ - next:8; /* The media type to try next. */ - short wait; /* Time before we check media status. */ -} media_tbl[] = { - { "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 }, - { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10}, - { "undefined", 0, 0x80, XCVR_10baseT, 10000}, - { "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10}, - { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10}, - { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10}, - { "MII", 0, 0x40, XCVR_10baseT, 3 * HZ}, - { "undefined", 0, 0x01, XCVR_10baseT, 10000}, - { "Default", 0, 0xFF, XCVR_10baseT, 10000}, -}; - -#ifdef __ISAPNP__ -static struct isapnp_device_id corkscrew_isapnp_adapters[] = { - { ISAPNP_ANY_ID, ISAPNP_ANY_ID, - ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051), - (long) "3Com Fast EtherLink ISA" }, - { } /* terminate list */ -}; - -MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters); - -static int nopnp; -#endif /* __ISAPNP__ */ - -static struct net_device *corkscrew_scan(int unit); -static int corkscrew_setup(struct net_device *dev, int ioaddr, - struct pnp_dev *idev, int card_number); -static int corkscrew_open(struct net_device *dev); -static void corkscrew_timer(struct timer_list *t); -static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static int corkscrew_rx(struct net_device *dev); -static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue); -static int boomerang_rx(struct net_device *dev); -static irqreturn_t corkscrew_interrupt(int irq, void *dev_id); -static int corkscrew_close(struct net_device *dev); -static void update_stats(int addr, struct net_device *dev); -static struct net_device_stats *corkscrew_get_stats(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static const struct ethtool_ops netdev_ethtool_ops; - - -/* - Unfortunately maximizing the shared code between the integrated and - module version of the driver results in a complicated set of initialization - procedures. - init_module() -- modules / tc59x_init() -- built-in - The wrappers for corkscrew_scan() - corkscrew_scan() The common routine that scans for PCI and EISA cards - corkscrew_found_device() Allocate a device structure when we find a card. - Different versions exist for modules and built-in. - corkscrew_probe1() Fill in the device structure -- this is separated - so that the modules code can put it in dev->init. -*/ -/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ -/* Note: this is the only limit on the number of cards supported!! */ -static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, }; - -#ifdef MODULE -static int debug = -1; - -module_param(debug, int, 0); -module_param_array(options, int, NULL, 0); -module_param(rx_copybreak, int, 0); -module_param(max_interrupt_work, int, 0); -MODULE_PARM_DESC(debug, "3c515 debug level (0-6)"); -MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering"); -MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt"); - -/* A list of all installed Vortex devices, for removing the driver module. */ -/* we will need locking (and refcounting) if we ever use it for more */ -static LIST_HEAD(root_corkscrew_dev); - -static int corkscrew_init_module(void) -{ - int found = 0; - if (debug >= 0) - corkscrew_debug = debug; - while (corkscrew_scan(-1)) - found++; - return found ? 0 : -ENODEV; -} -module_init(corkscrew_init_module); - -#else -struct net_device *tc515_probe(int unit) -{ - struct net_device *dev = corkscrew_scan(unit); - - if (!dev) - return ERR_PTR(-ENODEV); - - return dev; -} -#endif /* not MODULE */ - -static int check_device(unsigned ioaddr) -{ - int timer; - - if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515")) - return 0; - /* Check the resource configuration for a matching ioaddr. */ - if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) { - release_region(ioaddr, CORKSCREW_TOTAL_SIZE); - return 0; - } - /* Verify by reading the device ID from the EEPROM. */ - outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); - /* Pause for at least 162 us. for the read to take place. */ - for (timer = 4; timer >= 0; timer--) { - udelay(162); - if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) - break; - } - if (inw(ioaddr + Wn0EepromData) != 0x6d50) { - release_region(ioaddr, CORKSCREW_TOTAL_SIZE); - return 0; - } - return 1; -} - -static void cleanup_card(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - list_del_init(&vp->list); - if (dev->dma) - free_dma(dev->dma); - outw(TotalReset, dev->base_addr + EL3_CMD); - release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE); - if (vp->dev) - pnp_device_detach(to_pnp_dev(vp->dev)); -} - -static struct net_device *corkscrew_scan(int unit) -{ - struct net_device *dev; - static int cards_found = 0; - static int ioaddr; - int err; -#ifdef __ISAPNP__ - short i; - static int pnp_cards; -#endif - - dev = alloc_etherdev(sizeof(struct corkscrew_private)); - if (!dev) - return ERR_PTR(-ENOMEM); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - } - -#ifdef __ISAPNP__ - if(nopnp == 1) - goto no_pnp; - for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) { - struct pnp_dev *idev = NULL; - int irq; - while((idev = pnp_find_dev(NULL, - corkscrew_isapnp_adapters[i].vendor, - corkscrew_isapnp_adapters[i].function, - idev))) { - - if (pnp_device_attach(idev) < 0) - continue; - if (pnp_activate_dev(idev) < 0) { - pr_warn("pnp activate failed (out of resources?)\n"); - pnp_device_detach(idev); - continue; - } - if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) { - pnp_device_detach(idev); - continue; - } - ioaddr = pnp_port_start(idev, 0); - irq = pnp_irq(idev, 0); - if (!check_device(ioaddr)) { - pnp_device_detach(idev); - continue; - } - if(corkscrew_debug) - pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n", - (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq); - pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", - inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); - /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */ - SET_NETDEV_DEV(dev, &idev->dev); - pnp_cards++; - err = corkscrew_setup(dev, ioaddr, idev, cards_found++); - if (!err) - return dev; - cleanup_card(dev); - } - } -no_pnp: -#endif /* __ISAPNP__ */ - - /* Check all locations on the ISA bus -- evil! */ - for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { - if (!check_device(ioaddr)) - continue; - - pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", - inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); - err = corkscrew_setup(dev, ioaddr, NULL, cards_found++); - if (!err) - return dev; - cleanup_card(dev); - } - free_netdev(dev); - return NULL; -} - - -static const struct net_device_ops netdev_ops = { - .ndo_open = corkscrew_open, - .ndo_stop = corkscrew_close, - .ndo_start_xmit = corkscrew_start_xmit, - .ndo_tx_timeout = corkscrew_timeout, - .ndo_get_stats = corkscrew_get_stats, - .ndo_set_rx_mode = set_rx_mode, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - - -static int corkscrew_setup(struct net_device *dev, int ioaddr, - struct pnp_dev *idev, int card_number) -{ - struct corkscrew_private *vp = netdev_priv(dev); - unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ - __be16 addr[ETH_ALEN / 2]; - int i; - int irq; - -#ifdef __ISAPNP__ - if (idev) { - irq = pnp_irq(idev, 0); - vp->dev = &idev->dev; - } else { - irq = inw(ioaddr + 0x2002) & 15; - } -#else - irq = inw(ioaddr + 0x2002) & 15; -#endif - - dev->base_addr = ioaddr; - dev->irq = irq; - dev->dma = inw(ioaddr + 0x2000) & 7; - vp->product_name = "3c515"; - vp->options = dev->mem_start; - vp->our_dev = dev; - - if (!vp->options) { - if (card_number >= MAX_UNITS) - vp->options = -1; - else - vp->options = options[card_number]; - } - - if (vp->options >= 0) { - vp->media_override = vp->options & 7; - if (vp->media_override == 2) - vp->media_override = 0; - vp->full_duplex = (vp->options & 8) ? 1 : 0; - vp->bus_master = (vp->options & 16) ? 1 : 0; - } else { - vp->media_override = 7; - vp->full_duplex = 0; - vp->bus_master = 0; - } -#ifdef MODULE - list_add(&vp->list, &root_corkscrew_dev); -#endif - - pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr); - - spin_lock_init(&vp->lock); - - timer_setup(&vp->timer, corkscrew_timer, 0); - - /* Read the station address from the EEPROM. */ - EL3WINDOW(0); - for (i = 0; i < 0x18; i++) { - int timer; - outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); - /* Pause for at least 162 us. for the read to take place. */ - for (timer = 4; timer >= 0; timer--) { - udelay(162); - if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) - break; - } - eeprom[i] = inw(ioaddr + Wn0EepromData); - checksum ^= eeprom[i]; - if (i < 3) - addr[i] = htons(eeprom[i]); - } - eth_hw_addr_set(dev, (u8 *)addr); - checksum = (checksum ^ (checksum >> 8)) & 0xff; - if (checksum != 0x00) - pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum); - pr_cont(" %pM", dev->dev_addr); - if (eeprom[16] == 0x11c7) { /* Corkscrew */ - if (request_dma(dev->dma, "3c515")) { - pr_cont(", DMA %d allocation failed", dev->dma); - dev->dma = 0; - } else - pr_cont(", DMA %d", dev->dma); - } - pr_cont(", IRQ %d\n", dev->irq); - /* Tell them about an invalid IRQ. */ - if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15)) - pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n"); - - { - static const char * const ram_split[] = { - "5:3", "3:1", "1:1", "3:5" - }; - __u32 config; - EL3WINDOW(3); - vp->available_media = inw(ioaddr + Wn3_Options); - config = inl(ioaddr + Wn3_Config); - if (corkscrew_debug > 1) - pr_info(" Internal config register is %4.4x, transceivers %#x.\n", - config, inw(ioaddr + Wn3_Options)); - pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", - 8 << config & Ram_size, - config & Ram_width ? "word" : "byte", - ram_split[(config & Ram_split) >> Ram_split_shift], - config & Autoselect ? "autoselect/" : "", - media_tbl[(config & Xcvr) >> Xcvr_shift].name); - vp->default_media = (config & Xcvr) >> Xcvr_shift; - vp->autoselect = config & Autoselect ? 1 : 0; - dev->if_port = vp->default_media; - } - if (vp->media_override != 7) { - pr_info(" Media override to transceiver type %d (%s).\n", - vp->media_override, - media_tbl[vp->media_override].name); - dev->if_port = vp->media_override; - } - - vp->capabilities = eeprom[16]; - vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; - /* Rx is broken at 10mbps, so we always disable it. */ - /* vp->full_bus_master_rx = 0; */ - vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; - - /* The 3c51x-specific entries in the device structure. */ - dev->netdev_ops = &netdev_ops; - dev->watchdog_timeo = (400 * HZ) / 1000; - dev->ethtool_ops = &netdev_ethtool_ops; - - return register_netdev(dev); -} - - -static int corkscrew_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - struct corkscrew_private *vp = netdev_priv(dev); - bool armtimer = false; - __u32 config; - int i; - - /* Before initializing select the active media port. */ - EL3WINDOW(3); - if (vp->full_duplex) - outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ - config = inl(ioaddr + Wn3_Config); - - if (vp->media_override != 7) { - if (corkscrew_debug > 1) - pr_info("%s: Media override to transceiver %d (%s).\n", - dev->name, vp->media_override, - media_tbl[vp->media_override].name); - dev->if_port = vp->media_override; - } else if (vp->autoselect) { - /* Find first available media type, starting with 100baseTx. */ - dev->if_port = 4; - while (!(vp->available_media & media_tbl[dev->if_port].mask)) - dev->if_port = media_tbl[dev->if_port].next; - - if (corkscrew_debug > 1) - pr_debug("%s: Initial media type %s.\n", - dev->name, media_tbl[dev->if_port].name); - armtimer = true; - } else - dev->if_port = vp->default_media; - - config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); - outl(config, ioaddr + Wn3_Config); - - if (corkscrew_debug > 1) { - pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n", - dev->name, config); - } - - outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - - outw(RxReset, ioaddr + EL3_CMD); - /* Wait a few ticks for the RxReset command to complete. */ - for (i = 20; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - - outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); - - /* Use the now-standard shared IRQ implementation. */ - if (vp->capabilities == 0x11c7) { - /* Corkscrew: Cannot share ISA resources. */ - if (dev->irq == 0 || - dev->dma == 0 || - request_irq(dev->irq, corkscrew_interrupt, 0, - vp->product_name, dev)) - return -EAGAIN; - enable_dma(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_CASCADE); - } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED, - vp->product_name, dev)) { - return -EAGAIN; - } - - if (armtimer) - mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait); - - if (corkscrew_debug > 1) { - EL3WINDOW(4); - pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n", - dev->name, dev->irq, inw(ioaddr + Wn4_Media)); - } - - /* Set the station address and mask in window 2 each time opened. */ - EL3WINDOW(2); - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - for (; i < 12; i += 2) - outw(0, ioaddr + i); - - if (dev->if_port == 3) - /* Start the thinnet transceiver. We should really wait 50ms... */ - outw(StartCoax, ioaddr + EL3_CMD); - EL3WINDOW(4); - outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) | - media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 10; i++) - inb(ioaddr + i); - inw(ioaddr + 10); - inw(ioaddr + 12); - /* New: On the Vortex we must also clear the BadSSD counter. */ - EL3WINDOW(4); - inb(ioaddr + 12); - /* ..and on the Boomerang we enable the extra statistics bits. */ - outw(0x0040, ioaddr + Wn4_NetDiag); - - /* Switch to register set 7 for normal use. */ - EL3WINDOW(7); - - if (vp->full_bus_master_rx) { /* Boomerang bus master. */ - vp->cur_rx = vp->dirty_rx = 0; - if (corkscrew_debug > 2) - pr_debug("%s: Filling in the Rx ring.\n", dev->name); - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb; - if (i < (RX_RING_SIZE - 1)) - vp->rx_ring[i].next = - isa_virt_to_bus(&vp->rx_ring[i + 1]); - else - vp->rx_ring[i].next = 0; - vp->rx_ring[i].status = 0; /* Clear complete bit. */ - vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000; - skb = netdev_alloc_skb(dev, PKT_BUF_SZ); - vp->rx_skbuff[i] = skb; - if (skb == NULL) - break; /* Bad news! */ - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[i].addr = isa_virt_to_bus(skb->data); - } - if (i != 0) - vp->rx_ring[i - 1].next = - isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ - outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); - } - if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ - vp->cur_tx = vp->dirty_tx = 0; - outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */ - /* Clear the Tx ring. */ - for (i = 0; i < TX_RING_SIZE; i++) - vp->tx_skbuff[i] = NULL; - outl(0, ioaddr + DownListPtr); - } - /* Set receiver mode: presumably accept b-case and phys addr only. */ - set_rx_mode(dev); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - - netif_start_queue(dev); - - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull | - (vp->full_bus_master_tx ? DownComplete : TxAvailable) | - (vp->full_bus_master_rx ? UpComplete : RxComplete) | - (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, - ioaddr + EL3_CMD); - - return 0; -} - -static void corkscrew_timer(struct timer_list *t) -{ -#ifdef AUTOMEDIA - struct corkscrew_private *vp = timer_container_of(vp, t, timer); - struct net_device *dev = vp->our_dev; - int ioaddr = dev->base_addr; - unsigned long flags; - int ok = 0; - - if (corkscrew_debug > 1) - pr_debug("%s: Media selection timer tick happened, %s.\n", - dev->name, media_tbl[dev->if_port].name); - - spin_lock_irqsave(&vp->lock, flags); - - { - int old_window = inw(ioaddr + EL3_CMD) >> 13; - int media_status; - EL3WINDOW(4); - media_status = inw(ioaddr + Wn4_Media); - switch (dev->if_port) { - case 0: - case 4: - case 5: /* 10baseT, 100baseTX, 100baseFX */ - if (media_status & Media_LnkBeat) { - ok = 1; - if (corkscrew_debug > 1) - pr_debug("%s: Media %s has link beat, %x.\n", - dev->name, - media_tbl[dev->if_port].name, - media_status); - } else if (corkscrew_debug > 1) - pr_debug("%s: Media %s is has no link beat, %x.\n", - dev->name, - media_tbl[dev->if_port].name, - media_status); - - break; - default: /* Other media types handled by Tx timeouts. */ - if (corkscrew_debug > 1) - pr_debug("%s: Media %s is has no indication, %x.\n", - dev->name, - media_tbl[dev->if_port].name, - media_status); - ok = 1; - } - if (!ok) { - __u32 config; - - do { - dev->if_port = - media_tbl[dev->if_port].next; - } - while (!(vp->available_media & media_tbl[dev->if_port].mask)); - - if (dev->if_port == 8) { /* Go back to default. */ - dev->if_port = vp->default_media; - if (corkscrew_debug > 1) - pr_debug("%s: Media selection failing, using default %s port.\n", - dev->name, - media_tbl[dev->if_port].name); - } else { - if (corkscrew_debug > 1) - pr_debug("%s: Media selection failed, now trying %s port.\n", - dev->name, - media_tbl[dev->if_port].name); - vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; - add_timer(&vp->timer); - } - outw((media_status & ~(Media_10TP | Media_SQE)) | - media_tbl[dev->if_port].media_bits, - ioaddr + Wn4_Media); - - EL3WINDOW(3); - config = inl(ioaddr + Wn3_Config); - config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); - outl(config, ioaddr + Wn3_Config); - - outw(dev->if_port == 3 ? StartCoax : StopCoax, - ioaddr + EL3_CMD); - } - EL3WINDOW(old_window); - } - - spin_unlock_irqrestore(&vp->lock, flags); - if (corkscrew_debug > 1) - pr_debug("%s: Media selection timer finished, %s.\n", - dev->name, media_tbl[dev->if_port].name); - -#endif /* AUTOMEDIA */ -} - -static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue) -{ - int i; - struct corkscrew_private *vp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n", - dev->name, inb(ioaddr + TxStatus), - inw(ioaddr + EL3_STATUS)); - /* Slight code bloat to be user friendly. */ - if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) - pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n", - dev->name); -#ifndef final_version - pr_debug(" Flags; bus-master %d, full %d; dirty %d current %d.\n", - vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, - vp->cur_tx); - pr_debug(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), - &vp->tx_ring[0]); - for (i = 0; i < TX_RING_SIZE; i++) { - pr_debug(" %d: %p length %8.8x status %8.8x\n", i, - &vp->tx_ring[i], - vp->tx_ring[i].length, vp->tx_ring[i].status); - } -#endif - /* Issue TX_RESET and TX_START commands. */ - outw(TxReset, ioaddr + EL3_CMD); - for (i = 20; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - outw(TxEnable, ioaddr + EL3_CMD); - netif_trans_update(dev); /* prevent tx timeout */ - dev->stats.tx_errors++; - dev->stats.tx_dropped++; - netif_wake_queue(dev); -} - -static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - int ioaddr = dev->base_addr; - - /* Block a timer-based transmit from overlapping. */ - - netif_stop_queue(dev); - - if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */ - /* Calculate the next Tx descriptor entry. */ - int entry = vp->cur_tx % TX_RING_SIZE; - struct boom_tx_desc *prev_entry; - unsigned long flags; - int i; - - if (vp->tx_full) /* No room to transmit with */ - return NETDEV_TX_BUSY; - if (vp->cur_tx != 0) - prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE]; - else - prev_entry = NULL; - if (corkscrew_debug > 3) - pr_debug("%s: Trying to send a packet, Tx index %d.\n", - dev->name, vp->cur_tx); - /* vp->tx_full = 1; */ - vp->tx_skbuff[entry] = skb; - vp->tx_ring[entry].next = 0; - vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data); - vp->tx_ring[entry].length = skb->len | 0x80000000; - vp->tx_ring[entry].status = skb->len | 0x80000000; - - spin_lock_irqsave(&vp->lock, flags); - outw(DownStall, ioaddr + EL3_CMD); - /* Wait for the stall to complete. */ - for (i = 20; i >= 0; i--) - if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) - break; - if (prev_entry) - prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]); - if (inl(ioaddr + DownListPtr) == 0) { - outl(isa_virt_to_bus(&vp->tx_ring[entry]), - ioaddr + DownListPtr); - queued_packet++; - } - outw(DownUnstall, ioaddr + EL3_CMD); - spin_unlock_irqrestore(&vp->lock, flags); - - vp->cur_tx++; - if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) - vp->tx_full = 1; - else { /* Clear previous interrupt enable. */ - if (prev_entry) - prev_entry->status &= ~0x80000000; - netif_wake_queue(dev); - } - return NETDEV_TX_OK; - } - /* Put out the doubleword header... */ - outl(skb->len, ioaddr + TX_FIFO); - dev->stats.tx_bytes += skb->len; -#ifdef VORTEX_BUS_MASTER - if (vp->bus_master) { - /* Set the bus-master controller to transfer the packet. */ - outl(isa_virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr); - outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); - vp->tx_skb = skb; - outw(StartDMADown, ioaddr + EL3_CMD); - /* queue will be woken at the DMADone interrupt. */ - } else { - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - dev_kfree_skb(skb); - if (inw(ioaddr + TxFree) > 1536) { - netif_wake_queue(dev); - } else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + (1536 >> 2), - ioaddr + EL3_CMD); - } -#else - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - dev_kfree_skb(skb); - if (inw(ioaddr + TxFree) > 1536) { - netif_wake_queue(dev); - } else - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD); -#endif /* bus master */ - - - /* Clear the Tx status stack. */ - { - short tx_status; - int i = 4; - - while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { - if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */ - if (corkscrew_debug > 2) - pr_debug("%s: Tx error, status %2.2x.\n", - dev->name, tx_status); - if (tx_status & 0x04) - dev->stats.tx_fifo_errors++; - if (tx_status & 0x38) - dev->stats.tx_aborted_errors++; - if (tx_status & 0x30) { - int j; - outw(TxReset, ioaddr + EL3_CMD); - for (j = 20; j >= 0; j--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - } - outw(TxEnable, ioaddr + EL3_CMD); - } - outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ - } - } - return NETDEV_TX_OK; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ - -static irqreturn_t corkscrew_interrupt(int irq, void *dev_id) -{ - /* Use the now-standard shared IRQ implementation. */ - struct net_device *dev = dev_id; - struct corkscrew_private *lp = netdev_priv(dev); - int ioaddr, status; - int latency; - int i = max_interrupt_work; - - ioaddr = dev->base_addr; - latency = inb(ioaddr + Timer); - - spin_lock(&lp->lock); - - status = inw(ioaddr + EL3_STATUS); - - if (corkscrew_debug > 4) - pr_debug("%s: interrupt, status %4.4x, timer %d.\n", - dev->name, status, latency); - if ((status & 0xE000) != 0xE000) { - static int donedidthis; - /* Some interrupt controllers store a bogus interrupt from boot-time. - Ignore a single early interrupt, but don't hang the machine for - other interrupt problems. */ - if (donedidthis++ > 100) { - pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", - dev->name, status, netif_running(dev)); - free_irq(dev->irq, dev); - dev->irq = -1; - } - } - - do { - if (corkscrew_debug > 5) - pr_debug("%s: In interrupt loop, status %4.4x.\n", - dev->name, status); - if (status & RxComplete) - corkscrew_rx(dev); - - if (status & TxAvailable) { - if (corkscrew_debug > 5) - pr_debug(" TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue(dev); - } - if (status & DownComplete) { - unsigned int dirty_tx = lp->dirty_tx; - - while (lp->cur_tx - dirty_tx > 0) { - int entry = dirty_tx % TX_RING_SIZE; - if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry])) - break; /* It still hasn't been processed. */ - if (lp->tx_skbuff[entry]) { - dev_consume_skb_irq(lp->tx_skbuff[entry]); - lp->tx_skbuff[entry] = NULL; - } - dirty_tx++; - } - lp->dirty_tx = dirty_tx; - outw(AckIntr | DownComplete, ioaddr + EL3_CMD); - if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { - lp->tx_full = 0; - netif_wake_queue(dev); - } - } -#ifdef VORTEX_BUS_MASTER - if (status & DMADone) { - outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ - dev_consume_skb_irq(lp->tx_skb); /* Release the transferred buffer */ - netif_wake_queue(dev); - } -#endif - if (status & UpComplete) { - boomerang_rx(dev); - outw(AckIntr | UpComplete, ioaddr + EL3_CMD); - } - if (status & (AdapterFailure | RxEarly | StatsFull)) { - /* Handle all uncommon interrupts at once. */ - if (status & RxEarly) { /* Rx early is unused. */ - corkscrew_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & StatsFull) { /* Empty statistics. */ - static int DoneDidThat; - if (corkscrew_debug > 4) - pr_debug("%s: Updating stats.\n", dev->name); - update_stats(ioaddr, dev); - /* DEBUG HACK: Disable statistics as an interrupt source. */ - /* This occurs when we have the wrong media type! */ - if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) { - int win, reg; - pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n", - dev->name); - for (win = 0; win < 8; win++) { - EL3WINDOW(win); - pr_notice("Vortex window %d:", win); - for (reg = 0; reg < 16; reg++) - pr_cont(" %2.2x", inb(ioaddr + reg)); - pr_cont("\n"); - } - EL3WINDOW(7); - outw(SetIntrEnb | TxAvailable | - RxComplete | AdapterFailure | - UpComplete | DownComplete | - TxComplete, ioaddr + EL3_CMD); - DoneDidThat++; - } - } - if (status & AdapterFailure) { - /* Adapter failure requires Rx reset and reinit. */ - outw(RxReset, ioaddr + EL3_CMD); - /* Set the Rx filter to the current state. */ - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ - outw(AckIntr | AdapterFailure, - ioaddr + EL3_CMD); - } - } - - if (--i < 0) { - pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n", - dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); - /* Disable all pending interrupts. */ - outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); - outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); - - } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); - - spin_unlock(&lp->lock); - - if (corkscrew_debug > 4) - pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status); - return IRQ_HANDLED; -} - -static int corkscrew_rx(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - int i; - short rx_status; - - if (corkscrew_debug > 5) - pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); - while ((rx_status = inw(ioaddr + RxStatus)) > 0) { - if (rx_status & 0x4000) { /* Error, update stats. */ - unsigned char rx_error = inb(ioaddr + RxErrors); - if (corkscrew_debug > 2) - pr_debug(" Rx error: status %2.2x.\n", - rx_error); - dev->stats.rx_errors++; - if (rx_error & 0x01) - dev->stats.rx_over_errors++; - if (rx_error & 0x02) - dev->stats.rx_length_errors++; - if (rx_error & 0x04) - dev->stats.rx_frame_errors++; - if (rx_error & 0x08) - dev->stats.rx_crc_errors++; - if (rx_error & 0x10) - dev->stats.rx_length_errors++; - } else { - /* The packet length: up to 4.5K!. */ - short pkt_len = rx_status & 0x1fff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5 + 2); - if (corkscrew_debug > 4) - pr_debug("Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - /* 'skb_put()' points to the start of sk_buff data area. */ - insl(ioaddr + RX_FIFO, - skb_put(skb, pkt_len), - (pkt_len + 3) >> 2); - outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - /* Wait a limited time to go to next packet. */ - for (i = 200; i >= 0; i--) - if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - continue; - } else if (corkscrew_debug) - pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); - } - outw(RxDiscard, ioaddr + EL3_CMD); - dev->stats.rx_dropped++; - /* Wait a limited time to skip this packet. */ - for (i = 200; i >= 0; i--) - if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) - break; - } - return 0; -} - -static int boomerang_rx(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - int entry = vp->cur_rx % RX_RING_SIZE; - int ioaddr = dev->base_addr; - int rx_status; - - if (corkscrew_debug > 5) - pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); - while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { - if (rx_status & RxDError) { /* Error, update stats. */ - unsigned char rx_error = rx_status >> 16; - if (corkscrew_debug > 2) - pr_debug(" Rx error: status %2.2x.\n", - rx_error); - dev->stats.rx_errors++; - if (rx_error & 0x01) - dev->stats.rx_over_errors++; - if (rx_error & 0x02) - dev->stats.rx_length_errors++; - if (rx_error & 0x04) - dev->stats.rx_frame_errors++; - if (rx_error & 0x08) - dev->stats.rx_crc_errors++; - if (rx_error & 0x10) - dev->stats.rx_length_errors++; - } else { - /* The packet length: up to 4.5K!. */ - short pkt_len = rx_status & 0x1fff; - struct sk_buff *skb; - - dev->stats.rx_bytes += pkt_len; - if (corkscrew_debug > 4) - pr_debug("Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - - /* Check if the packet is long enough to just accept without - copying to a properly sized skbuff. */ - if (pkt_len < rx_copybreak && - (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) { - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - /* 'skb_put()' points to the start of sk_buff data area. */ - skb_put_data(skb, - isa_bus_to_virt(vp->rx_ring[entry].addr), - pkt_len); - rx_copy++; - } else { - void *temp; - /* Pass up the skbuff already on the Rx ring. */ - skb = vp->rx_skbuff[entry]; - vp->rx_skbuff[entry] = NULL; - temp = skb_put(skb, pkt_len); - /* Remove this checking code for final release. */ - if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp) - pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n", - dev->name, - isa_bus_to_virt(vp->rx_ring[entry].addr), - skb->head, temp); - rx_nocopy++; - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - } - entry = (++vp->cur_rx) % RX_RING_SIZE; - } - /* Refill the Rx ring buffers. */ - for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) { - struct sk_buff *skb; - entry = vp->dirty_rx % RX_RING_SIZE; - if (vp->rx_skbuff[entry] == NULL) { - skb = netdev_alloc_skb(dev, PKT_BUF_SZ); - if (skb == NULL) - break; /* Bad news! */ - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data); - vp->rx_skbuff[entry] = skb; - } - vp->rx_ring[entry].status = 0; /* Clear complete bit. */ - } - return 0; -} - -static int corkscrew_close(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - int ioaddr = dev->base_addr; - int i; - - netif_stop_queue(dev); - - if (corkscrew_debug > 1) { - pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n", - dev->name, inw(ioaddr + EL3_STATUS), - inb(ioaddr + TxStatus)); - pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n", - dev->name, rx_nocopy, rx_copy, queued_packet); - } - - timer_delete_sync(&vp->timer); - - /* Turn off statistics ASAP. We update lp->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - if (dev->if_port == XCVR_10base2) - /* Turn off thinnet power. Green! */ - outw(StopCoax, ioaddr + EL3_CMD); - - free_irq(dev->irq, dev); - - outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); - - update_stats(ioaddr, dev); - if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ - outl(0, ioaddr + UpListPtr); - for (i = 0; i < RX_RING_SIZE; i++) - if (vp->rx_skbuff[i]) { - dev_kfree_skb(vp->rx_skbuff[i]); - vp->rx_skbuff[i] = NULL; - } - } - if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ - outl(0, ioaddr + DownListPtr); - for (i = 0; i < TX_RING_SIZE; i++) - if (vp->tx_skbuff[i]) { - dev_kfree_skb(vp->tx_skbuff[i]); - vp->tx_skbuff[i] = NULL; - } - } - - return 0; -} - -static struct net_device_stats *corkscrew_get_stats(struct net_device *dev) -{ - struct corkscrew_private *vp = netdev_priv(dev); - unsigned long flags; - - if (netif_running(dev)) { - spin_lock_irqsave(&vp->lock, flags); - update_stats(dev->base_addr, dev); - spin_unlock_irqrestore(&vp->lock, flags); - } - return &dev->stats; -} - -/* Update statistics. - Unlike with the EL3 we need not worry about interrupts changing - the window setting from underneath us, but we must still guard - against a race condition with a StatsUpdate interrupt updating the - table. This is done by checking that the ASM (!) code generated uses - atomic updates with '+='. - */ -static void update_stats(int ioaddr, struct net_device *dev) -{ - /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; - /* Rx packets */ inb(ioaddr + 7); - /* Must read to clear */ - /* Tx deferrals */ inb(ioaddr + 8); - /* Don't bother with register 9, an extension of registers 6&7. - If we do use the 6&7 values the atomic update assumption above - is invalid. */ - inw(ioaddr + 10); /* Total Rx and Tx octets. */ - inw(ioaddr + 12); - /* New: On the Vortex we must also clear the BadSSD counter. */ - EL3WINDOW(4); - inb(ioaddr + 12); - - /* We change back to window 7 (not 1) with the Vortex. */ - EL3WINDOW(7); -} - -/* This new version of set_rx_mode() supports v1.4 kernels. - The Vortex chip has no documented multicast filter, so the only - multicast setting is to receive all multicast frames. At least - the chip has a very clean way to set the mode, unlike many others. */ -static void set_rx_mode(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - unsigned short new_mode; - - if (dev->flags & IFF_PROMISC) { - if (corkscrew_debug > 3) - pr_debug("%s: Setting promiscuous mode.\n", - dev->name); - new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm; - } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) { - new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast; - } else - new_mode = SetRxFilter | RxStation | RxBroadcast; - - outw(new_mode, ioaddr + EL3_CMD); -} - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", - dev->base_addr); -} - -static u32 netdev_get_msglevel(struct net_device *dev) -{ - return corkscrew_debug; -} - -static void netdev_set_msglevel(struct net_device *dev, u32 level) -{ - corkscrew_debug = level; -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, - .get_msglevel = netdev_get_msglevel, - .set_msglevel = netdev_set_msglevel, -}; - -#ifdef MODULE -static void __exit corkscrew_exit_module(void) -{ - while (!list_empty(&root_corkscrew_dev)) { - struct net_device *dev; - struct corkscrew_private *vp; - - vp = list_entry(root_corkscrew_dev.next, - struct corkscrew_private, list); - dev = vp->our_dev; - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } -} -module_exit(corkscrew_exit_module); -#endif /* MODULE */ diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c deleted file mode 100644 index 1f2070497a75..000000000000 --- a/drivers/net/ethernet/3com/3c574_cs.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner". - - Written 1993-1998 by - Donald Becker, becker@scyld.com, (driver core) and - David Hinds, dahinds@users.sourceforge.net (from his PC card code). - Locking fixes (C) Copyright 2003 Red Hat Inc - - This software may be used and distributed according to the terms of - the GNU General Public License, incorporated herein by reference. - - This driver derives from Donald Becker's 3c509 core, which has the - following copyright: - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - -*/ - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the 3Com 3c574 PC card Fast Ethernet -Adapter. - -II. Board-specific settings - -None -- PC cards are autoconfigured. - -III. Driver operation - -The 3c574 uses a Boomerang-style interface, without the bus-master capability. -See the Boomerang driver and documentation for most details. - -IV. Notes and chip documentation. - -Two added registers are used to enhance PIO performance, RunnerRdCtrl and -RunnerWrCtrl. These are 11 bit down-counters that are preloaded with the -count of word (16 bits) reads or writes the driver is about to do to the Rx -or Tx FIFO. The chip is then able to hide the internal-PCI-bus to PC-card -translation latency by buffering the I/O operations with an 8 word FIFO. -Note: No other chip accesses are permitted when this buffer is used. - -A second enhancement is that both attribute and common memory space -0x0800-0x0fff can translated to the PIO FIFO. Thus memory operations (faster -with *some* PCcard bridges) may be used instead of I/O operations. -This is enabled by setting the 0x10 bit in the PCMCIA LAN COR. - -Some slow PC card bridges work better if they never see a WAIT signal. -This is configured by setting the 0x20 bit in the PCMCIA LAN COR. -Only do this after testing that it is reliable and improves performance. - -The upper five bits of RunnerRdCtrl are used to window into PCcard -configuration space registers. Window 0 is the regular Boomerang/Odie -register set, 1-5 are various PC card control registers, and 16-31 are -the (reversed!) CIS table. - -A final note: writing the InternalConfig register in window 3 with an -invalid ramWidth is Very Bad. - -V. References - -http://www.scyld.com/expert/NWay.html -http://www.national.com/opf/DP/DP83840A.html - -Thanks to Terry Murphy of 3Com for providing development information for -earlier 3Com products. - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/delay.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ioport.h> -#include <linux/bitops.h> -#include <linux/mii.h> - -#include <pcmcia/cistpl.h> -#include <pcmcia/cisreg.h> -#include <pcmcia/ciscode.h> -#include <pcmcia/ds.h> - -#include <linux/uaccess.h> -#include <asm/io.h> - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); -MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -INT_MODULE_PARM(max_interrupt_work, 32); - -/* Force full duplex modes? */ -INT_MODULE_PARM(full_duplex, 0); - -/* Autodetect link polarity reversal? */ -INT_MODULE_PARM(auto_polarity, 1); - - -/*====================================================================*/ - -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((800*HZ)/1000) - -/* To minimize the size of the driver source and make the driver more - readable not all constants are symbolically defined. - You'll need the manual if you want to understand driver details anyway. */ -/* Offsets from base I/O address. */ -#define EL3_DATA 0x00 -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e - -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) - -/* The top five bits written to EL3_CMD are a command, the lower - 11 bits are the parameter, if applicable. */ -enum el3_cmds { - TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11, - RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11, - TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11, - FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11, - SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11, - SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11, - StatsDisable = 22<<11, StopCoax = 23<<11, -}; - -enum elxl_status { - IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, - TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, - IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 }; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 -}; - -enum Window0 { - Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */ - IntrStatus=0x0E, /* Valid in all windows. */ -}; -/* These assumes the larger EEPROM. */ -enum Win0_EEPROM_cmds { - EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300, - EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ - EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ -}; - -/* Register window 1 offsets, the window used in normal operation. - On the "Odie" this window is always mapped at offsets 0x10-0x1f. - Except for TxFree, which is overlapped by RunnerWrCtrl. */ -enum Window1 { - TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, - RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B, - TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */ - RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c, -}; - -enum Window3 { /* Window 3: MAC/config bits. */ - Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8, -}; -enum wn3_config { - Ram_size = 7, - Ram_width = 8, - Ram_speed = 0x30, - Rom_size = 0xc0, - Ram_split_shift = 16, - Ram_split = 3 << Ram_split_shift, - Xcvr_shift = 20, - Xcvr = 7 << Xcvr_shift, - Autoselect = 0x1000000, -}; - -enum Window4 { /* Window 4: Xcvr/media bits. */ - Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10, -}; - -#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ - -struct el3_private { - struct pcmcia_device *p_dev; - u16 advertising, partner; /* NWay media advertisement */ - unsigned char phys; /* MII device address */ - unsigned int autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */ - /* for transceiver monitoring */ - struct timer_list media; - unsigned short media_status; - unsigned short fast_poll; - unsigned long last_irq; - spinlock_t window_lock; /* Guards the Window selection */ -}; - -/* Set iff a MII transceiver on any interface requires mdio preamble. - This only set with the original DP83840 on older 3c905 boards, so the extra - code size of a per-interface flag is not worthwhile. */ -static char mii_preamble_required = 0; - -/* Index of functions. */ - -static int tc574_config(struct pcmcia_device *link); -static void tc574_release(struct pcmcia_device *link); - -static void mdio_sync(unsigned int ioaddr, int bits); -static int mdio_read(unsigned int ioaddr, int phy_id, int location); -static void mdio_write(unsigned int ioaddr, int phy_id, int location, - int value); -static unsigned short read_eeprom(unsigned int ioaddr, int index); -static void tc574_wait_for_completion(struct net_device *dev, int cmd); - -static void tc574_reset(struct net_device *dev); -static void media_check(struct timer_list *t); -static int el3_open(struct net_device *dev); -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t el3_interrupt(int irq, void *dev_id); -static void update_stats(struct net_device *dev); -static struct net_device_stats *el3_get_stats(struct net_device *dev); -static int el3_rx(struct net_device *dev, int worklimit); -static int el3_close(struct net_device *dev); -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue); -static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void set_rx_mode(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); - -static void tc574_detach(struct pcmcia_device *p_dev); - -/* - tc574_attach() creates an "instance" of the driver, allocating - local data structures for one device. The device is registered - with Card Services. -*/ -static const struct net_device_ops el3_netdev_ops = { - .ndo_open = el3_open, - .ndo_stop = el3_close, - .ndo_start_xmit = el3_start_xmit, - .ndo_tx_timeout = el3_tx_timeout, - .ndo_get_stats = el3_get_stats, - .ndo_eth_ioctl = el3_ioctl, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int tc574_probe(struct pcmcia_device *link) -{ - struct el3_private *lp; - struct net_device *dev; - - dev_dbg(&link->dev, "3c574_attach()\n"); - - /* Create the PC card device object. */ - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - link->priv = dev; - lp->p_dev = link; - - spin_lock_init(&lp->window_lock); - link->resource[0]->end = 32; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - link->config_flags |= CONF_ENABLE_IRQ; - link->config_index = 1; - - dev->netdev_ops = &el3_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - return tc574_config(link); -} - -static void tc574_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "3c574_detach()\n"); - - unregister_netdev(dev); - - tc574_release(link); - - free_netdev(dev); -} /* tc574_detach */ - -static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; - -static int tc574_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct el3_private *lp = netdev_priv(dev); - int ret, i, j; - __be16 addr[ETH_ALEN / 2]; - unsigned int ioaddr; - char *cardname; - __u32 config; - u8 *buf; - size_t len; - - dev_dbg(&link->dev, "3c574_config()\n"); - - link->io_lines = 16; - - for (i = j = 0; j < 0x400; j += 0x20) { - link->resource[0]->start = j ^ 0x300; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - if (i != 0) - goto failed; - - ret = pcmcia_request_irq(link, el3_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - ioaddr = dev->base_addr; - - /* The 3c574 normally uses an EEPROM for configuration info, including - the hardware address. The future products may include a modem chip - and put the address in the CIS. */ - - len = pcmcia_get_tuple(link, 0x88, &buf); - if (buf && len >= 6) { - for (i = 0; i < 3; i++) - addr[i] = htons(le16_to_cpu(buf[i * 2])); - kfree(buf); - } else { - kfree(buf); /* 0 < len < 6 */ - EL3WINDOW(0); - for (i = 0; i < 3; i++) - addr[i] = htons(read_eeprom(ioaddr, i + 10)); - if (addr[0] == htons(0x6060)) { - pr_notice("IO port conflict at 0x%03lx-0x%03lx\n", - dev->base_addr, dev->base_addr+15); - goto failed; - } - } - eth_hw_addr_set(dev, (u8 *)addr); - if (link->prod_id[1]) - cardname = link->prod_id[1]; - else - cardname = "3Com 3c574"; - - { - u_char mcr; - outw(2<<11, ioaddr + RunnerRdCtrl); - mcr = inb(ioaddr + 2); - outw(0<<11, ioaddr + RunnerRdCtrl); - pr_info(" ASIC rev %d,", mcr>>3); - EL3WINDOW(3); - config = inl(ioaddr + Wn3_Config); - lp->default_media = (config & Xcvr) >> Xcvr_shift; - lp->autoselect = config & Autoselect ? 1 : 0; - } - - timer_setup(&lp->media, media_check, 0); - - { - int phy; - - /* Roadrunner only: Turn on the MII transceiver */ - outw(0x8040, ioaddr + Wn3_Options); - mdelay(1); - outw(0xc040, ioaddr + Wn3_Options); - tc574_wait_for_completion(dev, TxReset); - tc574_wait_for_completion(dev, RxReset); - mdelay(1); - outw(0x8040, ioaddr + Wn3_Options); - - EL3WINDOW(4); - for (phy = 1; phy <= 32; phy++) { - int mii_status; - mdio_sync(ioaddr, 32); - mii_status = mdio_read(ioaddr, phy & 0x1f, 1); - if (mii_status != 0xffff) { - lp->phys = phy & 0x1f; - dev_dbg(&link->dev, " MII transceiver at " - "index %d, status %x.\n", - phy, mii_status); - if ((mii_status & 0x0040) == 0) - mii_preamble_required = 1; - break; - } - } - if (phy > 32) { - pr_notice(" No MII transceivers found!\n"); - goto failed; - } - i = mdio_read(ioaddr, lp->phys, 16) | 0x40; - mdio_write(ioaddr, lp->phys, 16, i); - lp->advertising = mdio_read(ioaddr, lp->phys, 4); - if (full_duplex) { - /* Only advertise the FD media types. */ - lp->advertising &= ~0x02a0; - mdio_write(ioaddr, lp->phys, 4, lp->advertising); - } - } - - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n", - cardname, dev->base_addr, dev->irq, dev->dev_addr); - netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n", - 8 << (config & Ram_size), - ram_split[(config & Ram_split) >> Ram_split_shift], - config & Autoselect ? "autoselect " : ""); - - return 0; - -failed: - tc574_release(link); - return -ENODEV; - -} /* tc574_config */ - -static void tc574_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int tc574_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int tc574_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - tc574_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - -static void dump_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - EL3WINDOW(1); - netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x, tx free %04x\n", - inw(ioaddr+EL3_STATUS), - inw(ioaddr+RxStatus), inb(ioaddr+TxStatus), - inw(ioaddr+TxFree)); - EL3WINDOW(4); - netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n", - inw(ioaddr+0x04), inw(ioaddr+0x06), - inw(ioaddr+0x08), inw(ioaddr+0x0a)); - EL3WINDOW(1); -} - -/* - Use this for commands that may take time to finish -*/ -static void tc574_wait_for_completion(struct net_device *dev, int cmd) -{ - int i = 1500; - outw(cmd, dev->base_addr + EL3_CMD); - while (--i > 0) - if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; - if (i == 0) - netdev_notice(dev, "command 0x%04x did not complete!\n", cmd); -} - -/* Read a word from the EEPROM using the regular EEPROM access register. - Assume that we are in register window zero. - */ -static unsigned short read_eeprom(unsigned int ioaddr, int index) -{ - int timer; - outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd); - /* Pause for at least 162 usec for the read to take place. */ - for (timer = 1620; timer >= 0; timer--) { - if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) - break; - } - return inw(ioaddr + Wn0EepromData); -} - -/* MII transceiver control section. - Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. - The maxium data clock rate is 2.5 Mhz. The timing is easily met by the - slow PC card interface. */ - -#define MDIO_SHIFT_CLK 0x01 -#define MDIO_DIR_WRITE 0x04 -#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE) -#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE) -#define MDIO_DATA_READ 0x02 -#define MDIO_ENB_IN 0x00 - -/* Generate the preamble required for initial synchronization and - a few older transceivers. */ -static void mdio_sync(unsigned int ioaddr, int bits) -{ - unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - - /* Establish sync by sending at least 32 logic ones. */ - while (-- bits >= 0) { - outw(MDIO_DATA_WRITE1, mdio_addr); - outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); - } -} - -static int mdio_read(unsigned int ioaddr, int phy_id, int location) -{ - int i; - int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - unsigned int retval = 0; - unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - - if (mii_preamble_required) - mdio_sync(ioaddr, 32); - - /* Shift the read command bits out. */ - for (i = 14; i >= 0; i--) { - int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; - outw(dataval, mdio_addr); - outw(dataval | MDIO_SHIFT_CLK, mdio_addr); - } - /* Read the two transition, 16 data, and wire-idle bits. */ - for (i = 19; i > 0; i--) { - outw(MDIO_ENB_IN, mdio_addr); - retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); - outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(unsigned int ioaddr, int phy_id, int location, int value) -{ - int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; - unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt; - int i; - - if (mii_preamble_required) - mdio_sync(ioaddr, 32); - - /* Shift the command bits out. */ - for (i = 31; i >= 0; i--) { - int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; - outw(dataval, mdio_addr); - outw(dataval | MDIO_SHIFT_CLK, mdio_addr); - } - /* Leave the interface idle. */ - for (i = 1; i >= 0; i--) { - outw(MDIO_ENB_IN, mdio_addr); - outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - } -} - -/* Reset and restore all of the 3c574 registers. */ -static void tc574_reset(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - int i; - unsigned int ioaddr = dev->base_addr; - unsigned long flags; - - tc574_wait_for_completion(dev, TotalReset|0x10); - - spin_lock_irqsave(&lp->window_lock, flags); - /* Clear any transactions in progress. */ - outw(0, ioaddr + RunnerWrCtrl); - outw(0, ioaddr + RunnerRdCtrl); - - /* Set the station address and mask. */ - EL3WINDOW(2); - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - for (; i < 12; i+=2) - outw(0, ioaddr + i); - - /* Reset config options */ - EL3WINDOW(3); - outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); - outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b, - ioaddr + Wn3_Config); - /* Roadrunner only: Turn on the MII transceiver. */ - outw(0x8040, ioaddr + Wn3_Options); - mdelay(1); - outw(0xc040, ioaddr + Wn3_Options); - EL3WINDOW(1); - spin_unlock_irqrestore(&lp->window_lock, flags); - - tc574_wait_for_completion(dev, TxReset); - tc574_wait_for_completion(dev, RxReset); - mdelay(1); - spin_lock_irqsave(&lp->window_lock, flags); - EL3WINDOW(3); - outw(0x8040, ioaddr + Wn3_Options); - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 10; i++) - inb(ioaddr + i); - inw(ioaddr + 10); - inw(ioaddr + 12); - EL3WINDOW(4); - inb(ioaddr + 12); - inb(ioaddr + 13); - - /* .. enable any extra statistics bits.. */ - outw(0x0040, ioaddr + Wn4_NetDiag); - - EL3WINDOW(1); - spin_unlock_irqrestore(&lp->window_lock, flags); - - /* .. re-sync MII and re-fill what NWay is advertising. */ - mdio_sync(ioaddr, 32); - mdio_write(ioaddr, lp->phys, 4, lp->advertising); - if (!auto_polarity) { - /* works for TDK 78Q2120 series MII's */ - i = mdio_read(ioaddr, lp->phys, 16) | 0x20; - mdio_write(ioaddr, lp->phys, 16, i); - } - - spin_lock_irqsave(&lp->window_lock, flags); - /* Switch to register set 1 for normal use, just for TxFree. */ - set_rx_mode(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | AdapterFailure | RxEarly, ioaddr + EL3_CMD); -} - -static int el3_open(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - netif_start_queue(dev); - - tc574_reset(dev); - lp->media.expires = jiffies + HZ; - add_timer(&lp->media); - - dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", - dev->name, inw(dev->base_addr + EL3_STATUS)); - - return 0; -} - -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - unsigned int ioaddr = dev->base_addr; - - netdev_notice(dev, "Transmit timed out!\n"); - dump_status(dev); - dev->stats.tx_errors++; - netif_trans_update(dev); /* prevent tx timeout */ - /* Issue TX_RESET and TX_START commands. */ - tc574_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - netif_wake_queue(dev); -} - -static void pop_tx_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int i; - - /* Clear the Tx status stack. */ - for (i = 32; i > 0; i--) { - u_char tx_status = inb(ioaddr + TxStatus); - if (!(tx_status & 0x84)) - break; - /* reset transmitter on jabber error or underrun */ - if (tx_status & 0x30) - tc574_wait_for_completion(dev, TxReset); - if (tx_status & 0x38) { - pr_debug("%s: transmit error: status 0x%02x\n", - dev->name, tx_status); - outw(TxEnable, ioaddr + EL3_CMD); - dev->stats.tx_aborted_errors++; - } - outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ - } -} - -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - - pr_debug("%s: el3_start_xmit(length = %ld) called, " - "status %4.4x.\n", dev->name, (long)skb->len, - inw(ioaddr + EL3_STATUS)); - - spin_lock_irqsave(&lp->window_lock, flags); - - dev->stats.tx_bytes += skb->len; - - /* Put out the doubleword header... */ - outw(skb->len, ioaddr + TX_FIFO); - outw(0, ioaddr + TX_FIFO); - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2); - - /* TxFree appears only in Window 1, not offset 0x1c. */ - if (inw(ioaddr + TxFree) <= 1536) { - netif_stop_queue(dev); - /* Interrupt us when the FIFO has room for max-sized packet. - The threshold is in units of dwords. */ - outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD); - } - - pop_tx_status(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - dev_kfree_skb(skb); - return NETDEV_TX_OK; -} - -/* The EL3 interrupt handler. */ -static irqreturn_t el3_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr; - unsigned status; - int work_budget = max_interrupt_work; - int handled = 0; - - if (!netif_device_present(dev)) - return IRQ_NONE; - ioaddr = dev->base_addr; - - pr_debug("%s: interrupt, status %4.4x.\n", - dev->name, inw(ioaddr + EL3_STATUS)); - - spin_lock(&lp->window_lock); - - while ((status = inw(ioaddr + EL3_STATUS)) & - (IntLatch | RxComplete | RxEarly | StatsFull)) { - if (!netif_device_present(dev) || - ((status & 0xe000) != 0x2000)) { - pr_debug("%s: Interrupt from dead card\n", dev->name); - break; - } - - handled = 1; - - if (status & RxComplete) - work_budget = el3_rx(dev, work_budget); - - if (status & TxAvailable) { - pr_debug(" TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue(dev); - } - - if (status & TxComplete) - pop_tx_status(dev); - - if (status & (AdapterFailure | RxEarly | StatsFull)) { - /* Handle all uncommon interrupts. */ - if (status & StatsFull) - update_stats(dev); - if (status & RxEarly) { - work_budget = el3_rx(dev, work_budget); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & AdapterFailure) { - u16 fifo_diag; - EL3WINDOW(4); - fifo_diag = inw(ioaddr + Wn4_FIFODiag); - EL3WINDOW(1); - netdev_notice(dev, "adapter failure, FIFO diagnostic register %04x\n", - fifo_diag); - if (fifo_diag & 0x0400) { - /* Tx overrun */ - tc574_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - } - if (fifo_diag & 0x2000) { - /* Rx underrun */ - tc574_wait_for_completion(dev, RxReset); - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); - } - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } - } - - if (--work_budget < 0) { - pr_debug("%s: Too much work in interrupt, " - "status %4.4x.\n", dev->name, status); - /* Clear all interrupts */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); - } - - pr_debug("%s: exiting interrupt, status %4.4x.\n", - dev->name, inw(ioaddr + EL3_STATUS)); - - spin_unlock(&lp->window_lock); - return IRQ_RETVAL(handled); -} - -/* - This timer serves two purposes: to check for missed interrupts - (and as a last resort, poll the NIC for events), and to monitor - the MII, reporting changes in cable status. -*/ -static void media_check(struct timer_list *t) -{ - struct el3_private *lp = timer_container_of(lp, t, media); - struct net_device *dev = lp->p_dev->priv; - unsigned int ioaddr = dev->base_addr; - unsigned long flags; - unsigned short /* cable, */ media, partner; - - if (!netif_device_present(dev)) - goto reschedule; - - /* Check for pending interrupt with expired latency timer: with - this, we can limp along even if the interrupt is blocked */ - if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) { - if (!lp->fast_poll) - netdev_info(dev, "interrupt(s) dropped!\n"); - - local_irq_save(flags); - el3_interrupt(dev->irq, dev); - local_irq_restore(flags); - - lp->fast_poll = HZ; - } - if (lp->fast_poll) { - lp->fast_poll--; - lp->media.expires = jiffies + 2*HZ/100; - add_timer(&lp->media); - return; - } - - spin_lock_irqsave(&lp->window_lock, flags); - EL3WINDOW(4); - media = mdio_read(ioaddr, lp->phys, 1); - partner = mdio_read(ioaddr, lp->phys, 5); - EL3WINDOW(1); - - if (media != lp->media_status) { - if ((media ^ lp->media_status) & 0x0004) - netdev_info(dev, "%s link beat\n", - (lp->media_status & 0x0004) ? "lost" : "found"); - if ((media ^ lp->media_status) & 0x0020) { - lp->partner = 0; - if (lp->media_status & 0x0020) { - netdev_info(dev, "autonegotiation restarted\n"); - } else if (partner) { - partner &= lp->advertising; - lp->partner = partner; - netdev_info(dev, "autonegotiation complete: " - "%dbaseT-%cD selected\n", - (partner & 0x0180) ? 100 : 10, - (partner & 0x0140) ? 'F' : 'H'); - } else { - netdev_info(dev, "link partner did not autonegotiate\n"); - } - - EL3WINDOW(3); - outb((partner & 0x0140 ? 0x20 : 0) | - (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl); - EL3WINDOW(1); - - } - if (media & 0x0010) - netdev_info(dev, "remote fault detected\n"); - if (media & 0x0002) - netdev_info(dev, "jabber detected\n"); - lp->media_status = media; - } - spin_unlock_irqrestore(&lp->window_lock, flags); - -reschedule: - lp->media.expires = jiffies + HZ; - add_timer(&lp->media); -} - -static struct net_device_stats *el3_get_stats(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - - if (netif_device_present(dev)) { - unsigned long flags; - spin_lock_irqsave(&lp->window_lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - } - return &dev->stats; -} - -/* Update statistics. - Surprisingly this need not be run single-threaded, but it effectively is. - The counters clear when read, so the adds must merely be atomic. - */ -static void update_stats(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u8 up; - - pr_debug("%s: updating the statistics.\n", dev->name); - - if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */ - return; - - /* Unlike the 3c509 we need not turn off stats updates while reading. */ - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - up = inb(ioaddr + 9); - dev->stats.tx_packets += (up&0x30) << 4; - /* Rx packets */ inb(ioaddr + 7); - /* Tx deferrals */ inb(ioaddr + 8); - /* rx */ inw(ioaddr + 10); - /* tx */ inw(ioaddr + 12); - - EL3WINDOW(4); - /* BadSSD */ inb(ioaddr + 12); - up = inb(ioaddr + 13); - - EL3WINDOW(1); -} - -static int el3_rx(struct net_device *dev, int worklimit) -{ - unsigned int ioaddr = dev->base_addr; - short rx_status; - - pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n", - dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus)); - while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) && - worklimit > 0) { - worklimit--; - if (rx_status & 0x4000) { /* Error, update stats. */ - short error = rx_status & 0x3800; - dev->stats.rx_errors++; - switch (error) { - case 0x0000: dev->stats.rx_over_errors++; break; - case 0x0800: dev->stats.rx_length_errors++; break; - case 0x1000: dev->stats.rx_frame_errors++; break; - case 0x1800: dev->stats.rx_length_errors++; break; - case 0x2000: dev->stats.rx_frame_errors++; break; - case 0x2800: dev->stats.rx_crc_errors++; break; - } - } else { - short pkt_len = rx_status & 0x7ff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5); - - pr_debug(" Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); - insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), - ((pkt_len+3)>>2)); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } else { - pr_debug("%s: couldn't allocate a sk_buff of" - " size %d.\n", dev->name, pkt_len); - dev->stats.rx_dropped++; - } - } - tc574_wait_for_completion(dev, RxDiscard); - } - - return worklimit; -} - -/* Provide ioctl() calls to examine the MII xcvr state. */ -static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - struct mii_ioctl_data *data = if_mii(rq); - int phy = lp->phys & 0x1f; - - pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n", - dev->name, rq->ifr_ifrn.ifrn_name, cmd, - data->phy_id, data->reg_num, data->val_in, data->val_out); - - switch(cmd) { - case SIOCGMIIPHY: /* Get the address of the PHY in use. */ - data->phy_id = phy; - fallthrough; - case SIOCGMIIREG: /* Read the specified MII register. */ - { - int saved_window; - unsigned long flags; - - spin_lock_irqsave(&lp->window_lock, flags); - saved_window = inw(ioaddr + EL3_CMD) >> 13; - EL3WINDOW(4); - data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f, - data->reg_num & 0x1f); - EL3WINDOW(saved_window); - spin_unlock_irqrestore(&lp->window_lock, flags); - return 0; - } - case SIOCSMIIREG: /* Write the specified MII register */ - { - int saved_window; - unsigned long flags; - - spin_lock_irqsave(&lp->window_lock, flags); - saved_window = inw(ioaddr + EL3_CMD) >> 13; - EL3WINDOW(4); - mdio_write(ioaddr, data->phy_id & 0x1f, - data->reg_num & 0x1f, data->val_in); - EL3WINDOW(saved_window); - spin_unlock_irqrestore(&lp->window_lock, flags); - return 0; - } - default: - return -EOPNOTSUPP; - } -} - -/* The Odie chip has a 64 bin multicast filter, but the bit layout is not - documented. Until it is we revert to receiving all multicast frames when - any multicast reception is desired. - Note: My other drivers emit a log message whenever promiscuous mode is - entered to help detect password sniffers. This is less desirable on - typical PC card machines, so we omit the message. - */ - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - - if (dev->flags & IFF_PROMISC) - outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm, - ioaddr + EL3_CMD); - else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI)) - outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD); - else - outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); -} - -static void set_multicast_list(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&lp->window_lock, flags); - set_rx_mode(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); -} - -static int el3_close(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); - - if (pcmcia_dev_present(link)) { - unsigned long flags; - - /* Turn off statistics ASAP. We update lp->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - /* Note: Switching to window 0 may disable the IRQ. */ - EL3WINDOW(0); - spin_lock_irqsave(&lp->window_lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->window_lock, flags); - - /* force interrupts off */ - outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); - } - - link->open--; - netif_stop_queue(dev); - timer_delete_sync(&lp->media); - - return 0; -} - -static const struct pcmcia_device_id tc574_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, tc574_ids); - -static struct pcmcia_driver tc574_driver = { - .owner = THIS_MODULE, - .name = "3c574_cs", - .probe = tc574_probe, - .remove = tc574_detach, - .id_table = tc574_ids, - .suspend = tc574_suspend, - .resume = tc574_resume, -}; -module_pcmcia_driver(tc574_driver); diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c deleted file mode 100644 index ea49be43b8c3..000000000000 --- a/drivers/net/ethernet/3com/3c589_cs.c +++ /dev/null @@ -1,974 +0,0 @@ -/* ====================================================================== - * - * A PCMCIA ethernet driver for the 3com 3c589 card. - * - * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net - * - * 3c589_cs.c 1.162 2001/10/13 00:08:50 - * - * The network driver code is based on Donald Becker's 3c589 code: - * - * Written 1994 by Donald Becker. - * Copyright 1993 United States Government as represented by the - * Director, National Security Agency. This software may be used and - * distributed according to the terms of the GNU General Public License, - * incorporated herein by reference. - * Donald Becker may be reached at becker@scyld.com - * - * Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk> - * - * ====================================================================== - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "3c589_cs" - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/delay.h> -#include <linux/ethtool.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ioport.h> -#include <linux/bitops.h> -#include <linux/jiffies.h> -#include <linux/uaccess.h> -#include <linux/io.h> - -#include <pcmcia/cistpl.h> -#include <pcmcia/cisreg.h> -#include <pcmcia/ciscode.h> -#include <pcmcia/ds.h> - - -/* To minimize the size of the driver source I only define operating - * constants if they are used several times. You'll need the manual - * if you want to understand driver details. - */ - -/* Offsets from base I/O address. */ -#define EL3_DATA 0x00 -#define EL3_TIMER 0x0a -#define EL3_CMD 0x0e -#define EL3_STATUS 0x0e - -#define EEPROM_READ 0x0080 -#define EEPROM_BUSY 0x8000 - -#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) - -/* The top five bits written to EL3_CMD are a command, the lower - * 11 bits are the parameter, if applicable. - */ - -enum c509cmd { - TotalReset = 0<<11, - SelectWindow = 1<<11, - StartCoax = 2<<11, - RxDisable = 3<<11, - RxEnable = 4<<11, - RxReset = 5<<11, - RxDiscard = 8<<11, - TxEnable = 9<<11, - TxDisable = 10<<11, - TxReset = 11<<11, - FakeIntr = 12<<11, - AckIntr = 13<<11, - SetIntrEnb = 14<<11, - SetStatusEnb = 15<<11, - SetRxFilter = 16<<11, - SetRxThreshold = 17<<11, - SetTxThreshold = 18<<11, - SetTxStart = 19<<11, - StatsEnable = 21<<11, - StatsDisable = 22<<11, - StopCoax = 23<<11 -}; - -enum c509status { - IntLatch = 0x0001, - AdapterFailure = 0x0002, - TxComplete = 0x0004, - TxAvailable = 0x0008, - RxComplete = 0x0010, - RxEarly = 0x0020, - IntReq = 0x0040, - StatsFull = 0x0080, - CmdBusy = 0x1000 -}; - -/* The SetRxFilter command accepts the following classes: */ -enum RxFilter { - RxStation = 1, - RxMulticast = 2, - RxBroadcast = 4, - RxProm = 8 -}; - -/* Register window 1 offsets, the window used in normal operation. */ -#define TX_FIFO 0x00 -#define RX_FIFO 0x00 -#define RX_STATUS 0x08 -#define TX_STATUS 0x0B -#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */ - -#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */ -#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */ -#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ -#define MEDIA_LED 0x0001 /* Enable link light on 3C589E cards. */ - -/* Time in jiffies before concluding Tx hung */ -#define TX_TIMEOUT ((400*HZ)/1000) - -struct el3_private { - struct pcmcia_device *p_dev; - /* For transceiver monitoring */ - struct timer_list media; - u16 media_status; - u16 fast_poll; - unsigned long last_irq; - spinlock_t lock; -}; - -static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" }; - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); -MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* Special hook for setting if_port when module is loaded */ -INT_MODULE_PARM(if_port, 0); - - -/*====================================================================*/ - -static int tc589_config(struct pcmcia_device *link); -static void tc589_release(struct pcmcia_device *link); - -static u16 read_eeprom(unsigned int ioaddr, int index); -static void tc589_reset(struct net_device *dev); -static void media_check(struct timer_list *t); -static int el3_config(struct net_device *dev, struct ifmap *map); -static int el3_open(struct net_device *dev); -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t el3_interrupt(int irq, void *dev_id); -static void update_stats(struct net_device *dev); -static struct net_device_stats *el3_get_stats(struct net_device *dev); -static int el3_rx(struct net_device *dev); -static int el3_close(struct net_device *dev); -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue); -static void set_rx_mode(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static const struct ethtool_ops netdev_ethtool_ops; - -static void tc589_detach(struct pcmcia_device *p_dev); - -static const struct net_device_ops el3_netdev_ops = { - .ndo_open = el3_open, - .ndo_stop = el3_close, - .ndo_start_xmit = el3_start_xmit, - .ndo_tx_timeout = el3_tx_timeout, - .ndo_set_config = el3_config, - .ndo_get_stats = el3_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int tc589_probe(struct pcmcia_device *link) -{ - struct el3_private *lp; - struct net_device *dev; - int ret; - - dev_dbg(&link->dev, "3c589_attach()\n"); - - /* Create new ethernet device */ - dev = alloc_etherdev(sizeof(struct el3_private)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - link->priv = dev; - lp->p_dev = link; - - spin_lock_init(&lp->lock); - link->resource[0]->end = 16; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16; - - link->config_flags |= CONF_ENABLE_IRQ; - link->config_index = 1; - - dev->netdev_ops = &el3_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->ethtool_ops = &netdev_ethtool_ops; - - ret = tc589_config(link); - if (ret) - goto err_free_netdev; - - return 0; - -err_free_netdev: - free_netdev(dev); - return ret; -} - -static void tc589_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "3c589_detach\n"); - - unregister_netdev(dev); - - tc589_release(link); - - free_netdev(dev); -} /* tc589_detach */ - -static int tc589_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - int ret, i, j, multi = 0, fifo; - __be16 addr[ETH_ALEN / 2]; - unsigned int ioaddr; - static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; - u8 *buf; - size_t len; - - dev_dbg(&link->dev, "3c589_config\n"); - - /* Is this a 3c562? */ - if (link->manf_id != MANFID_3COM) - dev_info(&link->dev, "hmmm, is this really a 3Com card??\n"); - multi = (link->card_id == PRODID_3COM_3C562); - - link->io_lines = 16; - - /* For the 3c562, the base address must be xx00-xx7f */ - for (i = j = 0; j < 0x400; j += 0x10) { - if (multi && (j & 0x80)) - continue; - link->resource[0]->start = j ^ 0x300; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - if (i != 0) - goto failed; - - ret = pcmcia_request_irq(link, el3_interrupt); - if (ret) - goto failed; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - ioaddr = dev->base_addr; - EL3WINDOW(0); - - /* The 3c589 has an extra EEPROM for configuration info, including - * the hardware address. The 3c562 puts the address in the CIS. - */ - len = pcmcia_get_tuple(link, 0x88, &buf); - if (buf && len >= 6) { - for (i = 0; i < 3; i++) - addr[i] = htons(le16_to_cpu(buf[i*2])); - kfree(buf); - } else { - kfree(buf); /* 0 < len < 6 */ - for (i = 0; i < 3; i++) - addr[i] = htons(read_eeprom(ioaddr, i)); - if (addr[0] == htons(0x6060)) { - dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n", - dev->base_addr, dev->base_addr+15); - goto failed; - } - } - eth_hw_addr_set(dev, (u8 *)addr); - - /* The address and resource configuration register aren't loaded from - * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. - */ - - outw(0x3f00, ioaddr + 8); - fifo = inl(ioaddr); - - /* The if_port symbol can be set when the module is loaded */ - if ((if_port >= 0) && (if_port <= 3)) - dev->if_port = if_port; - else - dev_err(&link->dev, "invalid if_port requested\n"); - - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - dev_err(&link->dev, "register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n", - (multi ? "562" : "589"), dev->base_addr, dev->irq, - dev->dev_addr); - netdev_info(dev, " %dK FIFO split %s Rx:Tx, %s xcvr\n", - (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3], - if_names[dev->if_port]); - return 0; - -failed: - tc589_release(link); - return -ENODEV; -} /* tc589_config */ - -static void tc589_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int tc589_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int tc589_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - tc589_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - -/*====================================================================*/ - -/* Use this for commands that may take time to finish */ - -static void tc589_wait_for_completion(struct net_device *dev, int cmd) -{ - int i = 100; - outw(cmd, dev->base_addr + EL3_CMD); - while (--i > 0) - if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) - break; - if (i == 0) - netdev_warn(dev, "command 0x%04x did not complete!\n", cmd); -} - -/* Read a word from the EEPROM using the regular EEPROM access register. - * Assume that we are in register window zero. - */ - -static u16 read_eeprom(unsigned int ioaddr, int index) -{ - int i; - outw(EEPROM_READ + index, ioaddr + 10); - /* Reading the eeprom takes 162 us */ - for (i = 1620; i >= 0; i--) - if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0) - break; - return inw(ioaddr + 12); -} - -/* Set transceiver type, perhaps to something other than what the user - * specified in dev->if_port. - */ - -static void tc589_set_xcvr(struct net_device *dev, int if_port) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - EL3WINDOW(0); - switch (if_port) { - case 0: - case 1: - outw(0, ioaddr + 6); - break; - case 2: - outw(3<<14, ioaddr + 6); - break; - case 3: - outw(1<<14, ioaddr + 6); - break; - } - /* On PCMCIA, this just turns on the LED */ - outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD); - /* 10baseT interface, enable link beat and jabber check. */ - EL3WINDOW(4); - outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA); - EL3WINDOW(1); - if (if_port == 2) - lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000); - else - lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800); -} - -static void dump_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - EL3WINDOW(1); - netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x tx free %04x\n", - inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS), - inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE)); - EL3WINDOW(4); - netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n", - inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08), - inw(ioaddr+0x0a)); - EL3WINDOW(1); -} - -/* Reset and restore all of the 3c589 registers. */ -static void tc589_reset(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int i; - - EL3WINDOW(0); - outw(0x0001, ioaddr + 4); /* Activate board. */ - outw(0x3f00, ioaddr + 8); /* Set the IRQ line. */ - - /* Set the station address in window 2. */ - EL3WINDOW(2); - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + i); - - tc589_set_xcvr(dev, dev->if_port); - - /* Switch to the stats window, and clear all stats by reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - EL3WINDOW(6); - for (i = 0; i < 9; i++) - inb(ioaddr+i); - inw(ioaddr + 10); - inw(ioaddr + 12); - - /* Switch to register set 1 for normal use. */ - EL3WINDOW(1); - - set_rx_mode(dev); - outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ - outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ - outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ - /* Allow status bits to be seen. */ - outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); - /* Ack all pending events, and set active indicator mask. */ - outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, - ioaddr + EL3_CMD); - outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull - | AdapterFailure, ioaddr + EL3_CMD); -} - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - snprintf(info->bus_info, sizeof(info->bus_info), - "PCMCIA 0x%lx", dev->base_addr); -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, -}; - -static int el3_config(struct net_device *dev, struct ifmap *map) -{ - if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { - if (map->port <= 3) { - WRITE_ONCE(dev->if_port, map->port); - netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); - tc589_set_xcvr(dev, dev->if_port); - } else { - return -EINVAL; - } - } - return 0; -} - -static int el3_open(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - netif_start_queue(dev); - - tc589_reset(dev); - timer_setup(&lp->media, media_check, 0); - mod_timer(&lp->media, jiffies + HZ); - - dev_dbg(&link->dev, "%s: opened, status %4.4x.\n", - dev->name, inw(dev->base_addr + EL3_STATUS)); - - return 0; -} - -static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - unsigned int ioaddr = dev->base_addr; - - netdev_warn(dev, "Transmit timed out!\n"); - dump_status(dev); - dev->stats.tx_errors++; - netif_trans_update(dev); /* prevent tx timeout */ - /* Issue TX_RESET and TX_START commands. */ - tc589_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - netif_wake_queue(dev); -} - -static void pop_tx_status(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int i; - - /* Clear the Tx status stack. */ - for (i = 32; i > 0; i--) { - u_char tx_status = inb(ioaddr + TX_STATUS); - if (!(tx_status & 0x84)) - break; - /* reset transmitter on jabber error or underrun */ - if (tx_status & 0x30) - tc589_wait_for_completion(dev, TxReset); - if (tx_status & 0x38) { - netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status); - outw(TxEnable, ioaddr + EL3_CMD); - dev->stats.tx_aborted_errors++; - } - outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */ - } -} - -static netdev_tx_t el3_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct el3_private *priv = netdev_priv(dev); - unsigned long flags; - - netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n", - (long)skb->len, inw(ioaddr + EL3_STATUS)); - - spin_lock_irqsave(&priv->lock, flags); - - dev->stats.tx_bytes += skb->len; - - /* Put out the doubleword header... */ - outw(skb->len, ioaddr + TX_FIFO); - outw(0x00, ioaddr + TX_FIFO); - /* ... and the packet rounded to a doubleword. */ - outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); - - if (inw(ioaddr + TX_FREE) <= 1536) { - netif_stop_queue(dev); - /* Interrupt us when the FIFO has room for max-sized packet. */ - outw(SetTxThreshold + 1536, ioaddr + EL3_CMD); - } - - pop_tx_status(dev); - spin_unlock_irqrestore(&priv->lock, flags); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -/* The EL3 interrupt handler. */ -static irqreturn_t el3_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - struct el3_private *lp = netdev_priv(dev); - unsigned int ioaddr; - __u16 status; - int i = 0, handled = 1; - - if (!netif_device_present(dev)) - return IRQ_NONE; - - ioaddr = dev->base_addr; - - netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS)); - - spin_lock(&lp->lock); - while ((status = inw(ioaddr + EL3_STATUS)) & - (IntLatch | RxComplete | StatsFull)) { - if ((status & 0xe000) != 0x2000) { - netdev_dbg(dev, "interrupt from dead card\n"); - handled = 0; - break; - } - if (status & RxComplete) - el3_rx(dev); - if (status & TxAvailable) { - netdev_dbg(dev, " TX room bit was handled.\n"); - /* There's room in the FIFO for a full-sized packet. */ - outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); - netif_wake_queue(dev); - } - if (status & TxComplete) - pop_tx_status(dev); - if (status & (AdapterFailure | RxEarly | StatsFull)) { - /* Handle all uncommon interrupts. */ - if (status & StatsFull) /* Empty statistics. */ - update_stats(dev); - if (status & RxEarly) { - /* Rx early is unused. */ - el3_rx(dev); - outw(AckIntr | RxEarly, ioaddr + EL3_CMD); - } - if (status & AdapterFailure) { - u16 fifo_diag; - EL3WINDOW(4); - fifo_diag = inw(ioaddr + 4); - EL3WINDOW(1); - netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n", - fifo_diag); - if (fifo_diag & 0x0400) { - /* Tx overrun */ - tc589_wait_for_completion(dev, TxReset); - outw(TxEnable, ioaddr + EL3_CMD); - } - if (fifo_diag & 0x2000) { - /* Rx underrun */ - tc589_wait_for_completion(dev, RxReset); - set_rx_mode(dev); - outw(RxEnable, ioaddr + EL3_CMD); - } - outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); - } - } - if (++i > 10) { - netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n", - status); - /* Clear all interrupts */ - outw(AckIntr | 0xFF, ioaddr + EL3_CMD); - break; - } - /* Acknowledge the IRQ. */ - outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); - } - lp->last_irq = jiffies; - spin_unlock(&lp->lock); - netdev_dbg(dev, "exiting interrupt, status %4.4x.\n", - inw(ioaddr + EL3_STATUS)); - return IRQ_RETVAL(handled); -} - -static void media_check(struct timer_list *t) -{ - struct el3_private *lp = timer_container_of(lp, t, media); - struct net_device *dev = lp->p_dev->priv; - unsigned int ioaddr = dev->base_addr; - u16 media, errs; - unsigned long flags; - - if (!netif_device_present(dev)) - goto reschedule; - - /* Check for pending interrupt with expired latency timer: with - * this, we can limp along even if the interrupt is blocked - */ - if ((inw(ioaddr + EL3_STATUS) & IntLatch) && - (inb(ioaddr + EL3_TIMER) == 0xff)) { - if (!lp->fast_poll) - netdev_warn(dev, "interrupt(s) dropped!\n"); - - local_irq_save(flags); - el3_interrupt(dev->irq, dev); - local_irq_restore(flags); - - lp->fast_poll = HZ; - } - if (lp->fast_poll) { - lp->fast_poll--; - lp->media.expires = jiffies + HZ/100; - add_timer(&lp->media); - return; - } - - /* lp->lock guards the EL3 window. Window should always be 1 except - * when the lock is held - */ - - spin_lock_irqsave(&lp->lock, flags); - EL3WINDOW(4); - media = inw(ioaddr+WN4_MEDIA) & 0xc810; - - /* Ignore collisions unless we've had no irq's recently */ - if (time_before(jiffies, lp->last_irq + HZ)) { - media &= ~0x0010; - } else { - /* Try harder to detect carrier errors */ - EL3WINDOW(6); - outw(StatsDisable, ioaddr + EL3_CMD); - errs = inb(ioaddr + 0); - outw(StatsEnable, ioaddr + EL3_CMD); - dev->stats.tx_carrier_errors += errs; - if (errs || (lp->media_status & 0x0010)) - media |= 0x0010; - } - - if (media != lp->media_status) { - if ((media & lp->media_status & 0x8000) && - ((lp->media_status ^ media) & 0x0800)) - netdev_info(dev, "%s link beat\n", - (lp->media_status & 0x0800 ? "lost" : "found")); - else if ((media & lp->media_status & 0x4000) && - ((lp->media_status ^ media) & 0x0010)) - netdev_info(dev, "coax cable %s\n", - (lp->media_status & 0x0010 ? "ok" : "problem")); - if (dev->if_port == 0) { - if (media & 0x8000) { - if (media & 0x0800) - netdev_info(dev, "flipped to 10baseT\n"); - else - tc589_set_xcvr(dev, 2); - } else if (media & 0x4000) { - if (media & 0x0010) - tc589_set_xcvr(dev, 1); - else - netdev_info(dev, "flipped to 10base2\n"); - } - } - lp->media_status = media; - } - - EL3WINDOW(1); - spin_unlock_irqrestore(&lp->lock, flags); - -reschedule: - lp->media.expires = jiffies + HZ; - add_timer(&lp->media); -} - -static struct net_device_stats *el3_get_stats(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - unsigned long flags; - struct pcmcia_device *link = lp->p_dev; - - if (pcmcia_dev_present(link)) { - spin_lock_irqsave(&lp->lock, flags); - update_stats(dev); - spin_unlock_irqrestore(&lp->lock, flags); - } - return &dev->stats; -} - -/* Update statistics. We change to register window 6, so this should be run -* single-threaded if the device is active. This is expected to be a rare -* operation, and it's simpler for the rest of the driver to assume that -* window 1 is always valid rather than use a special window-state variable. -* -* Caller must hold the lock for this -*/ - -static void update_stats(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - - netdev_dbg(dev, "updating the statistics.\n"); - /* Turn off statistics updates while reading. */ - outw(StatsDisable, ioaddr + EL3_CMD); - /* Switch to the stats window, and read everything. */ - EL3WINDOW(6); - dev->stats.tx_carrier_errors += inb(ioaddr + 0); - dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); - /* Multiple collisions. */ - inb(ioaddr + 2); - dev->stats.collisions += inb(ioaddr + 3); - dev->stats.tx_window_errors += inb(ioaddr + 4); - dev->stats.rx_fifo_errors += inb(ioaddr + 5); - dev->stats.tx_packets += inb(ioaddr + 6); - /* Rx packets */ - inb(ioaddr + 7); - /* Tx deferrals */ - inb(ioaddr + 8); - /* Rx octets */ - inw(ioaddr + 10); - /* Tx octets */ - inw(ioaddr + 12); - - /* Back to window 1, and turn statistics back on. */ - EL3WINDOW(1); - outw(StatsEnable, ioaddr + EL3_CMD); -} - -static int el3_rx(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int worklimit = 32; - short rx_status; - - netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n", - inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS)); - while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) && - worklimit > 0) { - worklimit--; - if (rx_status & 0x4000) { /* Error, update stats. */ - short error = rx_status & 0x3800; - dev->stats.rx_errors++; - switch (error) { - case 0x0000: - dev->stats.rx_over_errors++; - break; - case 0x0800: - dev->stats.rx_length_errors++; - break; - case 0x1000: - dev->stats.rx_frame_errors++; - break; - case 0x1800: - dev->stats.rx_length_errors++; - break; - case 0x2000: - dev->stats.rx_frame_errors++; - break; - case 0x2800: - dev->stats.rx_crc_errors++; - break; - } - } else { - short pkt_len = rx_status & 0x7ff; - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 5); - - netdev_dbg(dev, " Receiving packet size %d status %4.4x.\n", - pkt_len, rx_status); - if (skb != NULL) { - skb_reserve(skb, 2); - insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), - (pkt_len+3)>>2); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } else { - netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n", - pkt_len); - dev->stats.rx_dropped++; - } - } - /* Pop the top of the Rx FIFO */ - tc589_wait_for_completion(dev, RxDiscard); - } - if (worklimit == 0) - netdev_warn(dev, "too much work in el3_rx!\n"); - return 0; -} - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u16 opts = SetRxFilter | RxStation | RxBroadcast; - - if (dev->flags & IFF_PROMISC) - opts |= RxMulticast | RxProm; - else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI)) - opts |= RxMulticast; - outw(opts, ioaddr + EL3_CMD); -} - -static void set_multicast_list(struct net_device *dev) -{ - struct el3_private *priv = netdev_priv(dev); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - set_rx_mode(dev); - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int el3_close(struct net_device *dev) -{ - struct el3_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - unsigned int ioaddr = dev->base_addr; - - dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); - - if (pcmcia_dev_present(link)) { - /* Turn off statistics ASAP. We update dev->stats below. */ - outw(StatsDisable, ioaddr + EL3_CMD); - - /* Disable the receiver and transmitter. */ - outw(RxDisable, ioaddr + EL3_CMD); - outw(TxDisable, ioaddr + EL3_CMD); - - if (dev->if_port == 2) - /* Turn off thinnet power. Green! */ - outw(StopCoax, ioaddr + EL3_CMD); - else if (dev->if_port == 1) { - /* Disable link beat and jabber */ - EL3WINDOW(4); - outw(0, ioaddr + WN4_MEDIA); - } - - /* Switching back to window 0 disables the IRQ. */ - EL3WINDOW(0); - /* But we explicitly zero the IRQ line select anyway. */ - outw(0x0f00, ioaddr + WN0_IRQ); - - /* Check if the card still exists */ - if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000) - update_stats(dev); - } - - link->open--; - netif_stop_queue(dev); - timer_delete_sync(&lp->media); - - return 0; -} - -static const struct pcmcia_device_id tc589_ids[] = { - PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562), - PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77), - PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589), - PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, tc589_ids); - -static struct pcmcia_driver tc589_driver = { - .owner = THIS_MODULE, - .name = "3c589_cs", - .probe = tc589_probe, - .remove = tc589_detach, - .id_table = tc589_ids, - .suspend = tc589_suspend, - .resume = tc589_resume, -}; -module_pcmcia_driver(tc589_driver); diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig index 1fbab79e2be4..399cb6c56198 100644 --- a/drivers/net/ethernet/3com/Kconfig +++ b/drivers/net/ethernet/3com/Kconfig @@ -17,51 +17,6 @@ config NET_VENDOR_3COM if NET_VENDOR_3COM -config EL3 - tristate "3c509/3c579 \"EtherLink III\" support" - depends on (ISA || EISA) - help - If you have a network (Ethernet) card belonging to the 3Com - EtherLinkIII series, say Y here. - - If your card is not working you may need to use the DOS - setup disk to disable Plug & Play mode, and to select the default - media type. - - To compile this driver as a module, choose M here. The module - will be called 3c509. - -config 3C515 - tristate "3c515 ISA \"Fast EtherLink\"" - depends on ISA && ISA_DMA_API && !PPC32 - select NETDEV_LEGACY_INIT - help - If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet - network card, say Y here. - - To compile this driver as a module, choose M here. The module - will be called 3c515. - -config PCMCIA_3C574 - tristate "3Com 3c574 PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA - (PC-card) Fast Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called 3c574_cs. If unsure, say N. - -config PCMCIA_3C589 - tristate "3Com 3c589 PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA - (PC-card) Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called 3c589_cs. If unsure, say N. - config VORTEX tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support" depends on (PCI || EISA) && HAS_IOPORT_MAP diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile index f8b73babc510..5c4d07f1d456 100644 --- a/drivers/net/ethernet/3com/Makefile +++ b/drivers/net/ethernet/3com/Makefile @@ -3,9 +3,5 @@ # Makefile for the 3Com Ethernet device drivers # -obj-$(CONFIG_EL3) += 3c509.o -obj-$(CONFIG_3C515) += 3c515.o -obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o -obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o obj-$(CONFIG_VORTEX) += 3c59x.o obj-$(CONFIG_TYPHOON) += typhoon.o diff --git a/drivers/net/ethernet/8390/Kconfig b/drivers/net/ethernet/8390/Kconfig index 345f250781c6..5d12a595ab19 100644 --- a/drivers/net/ethernet/8390/Kconfig +++ b/drivers/net/ethernet/8390/Kconfig @@ -17,18 +17,6 @@ config NET_VENDOR_8390 if NET_VENDOR_8390 -config PCMCIA_AXNET - tristate "Asix AX88190 PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach an Asix AX88190-based PCMCIA - (PC-card) Fast Ethernet card to your computer. These cards are - nearly NE2000 compatible but need a separate driver due to a few - misfeatures. - - To compile this driver as a module, choose M here: the module will be - called axnet_cs. If unsure, say N. - config AX88796 tristate "ASIX AX88796 NE2000 clone support" if !ZORRO depends on (ARM || MIPS || SUPERH || ZORRO || COMPILE_TEST) @@ -167,35 +155,6 @@ config STNIC If unsure, say N. -config ULTRA - tristate "SMC Ultra support" - depends on ISA - select NETDEV_LEGACY_INIT - select CRC32 - help - If you have a network (Ethernet) card of this type, say Y here. - - Important: There have been many reports that, with some motherboards - mixing an SMC Ultra and an Adaptec AHA154x SCSI card (or compatible, - such as some BusLogic models) causes corruption problems with many - operating systems. The Linux smc-ultra driver has a work-around for - this but keep it in mind if you have such a SCSI card and have - problems. - - To compile this driver as a module, choose M here. The module - will be called smc-ultra. - -config WD80x3 - tristate "WD80*3 support" - depends on ISA - select NETDEV_LEGACY_INIT - select CRC32 - help - If you have a network (Ethernet) card of this type, say Y here. - - To compile this driver as a module, choose M here. The module - will be called wd. - config ZORRO8390 tristate "Zorro NS8390-based Ethernet support" depends on ZORRO diff --git a/drivers/net/ethernet/8390/Makefile b/drivers/net/ethernet/8390/Makefile index 85c83c566ec6..bca5babdadc7 100644 --- a/drivers/net/ethernet/8390/Makefile +++ b/drivers/net/ethernet/8390/Makefile @@ -11,10 +11,7 @@ obj-$(CONFIG_HYDRA) += hydra.o obj-$(CONFIG_MCF8390) += mcf8390.o obj-$(CONFIG_NE2000) += ne.o 8390p.o obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o -obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o 8390.o obj-$(CONFIG_PCMCIA_PCNET) += pcnet_cs.o 8390.o obj-$(CONFIG_STNIC) += stnic.o 8390.o -obj-$(CONFIG_ULTRA) += smc-ultra.o 8390.o -obj-$(CONFIG_WD80x3) += wd.o 8390.o obj-$(CONFIG_XSURF100) += xsurf100.o obj-$(CONFIG_ZORRO8390) += zorro8390.o diff --git a/drivers/net/ethernet/8390/axnet_cs.c b/drivers/net/ethernet/8390/axnet_cs.c deleted file mode 100644 index 7c8213011b5c..000000000000 --- a/drivers/net/ethernet/8390/axnet_cs.c +++ /dev/null @@ -1,1707 +0,0 @@ -// SPDX-License-Identifier: GPL-1.0+ - -/*====================================================================== - - A PCMCIA ethernet driver for Asix AX88190-based cards - - The Asix AX88190 is a NS8390-derived chipset with a few nasty - idiosyncracies that make it very inconvenient to support with a - standard 8390 driver. This driver is based on pcnet_cs, with the - tweaked 8390 code grafted on the end. Much of what I did was to - clean up and update a similar driver supplied by Asix, which was - adapted by William Lee, william@asix.com.tw. - - Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net - - axnet_cs.c 1.28 2002/06/29 06:27:37 - - The network driver code is based on Donald Becker's NE2000 code: - - Written 1992,1993 by Donald Becker. - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - Donald Becker may be reached at becker@scyld.com - -======================================================================*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/ptrace.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/crc32.h> -#include <linux/mii.h> -#include "8390.h" - -#include <pcmcia/cistpl.h> -#include <pcmcia/ciscode.h> -#include <pcmcia/ds.h> -#include <pcmcia/cisreg.h> - -#include <asm/io.h> -#include <asm/byteorder.h> -#include <linux/uaccess.h> - -#define AXNET_CMD 0x00 -#define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */ -#define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */ -#define AXNET_MII_EEP 0x14 /* Offset of MII access port */ -#define AXNET_TEST 0x15 /* Offset of TEST Register port */ -#define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */ - -#define AXNET_START_PG 0x40 /* First page of TX buffer */ -#define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */ - -#define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */ - -#define IS_AX88190 0x0001 -#define IS_AX88790 0x0002 - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); -MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - - -/*====================================================================*/ - -static int axnet_config(struct pcmcia_device *link); -static void axnet_release(struct pcmcia_device *link); -static int axnet_open(struct net_device *dev); -static int axnet_close(struct net_device *dev); -static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static netdev_tx_t axnet_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static struct net_device_stats *get_stats(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue); -static irqreturn_t ei_irq_wrapper(int irq, void *dev_id); -static void ei_watchdog(struct timer_list *t); -static void axnet_reset_8390(struct net_device *dev); - -static int mdio_read(unsigned int addr, int phy_id, int loc); -static void mdio_write(unsigned int addr, int phy_id, int loc, int value); - -static void get_8390_hdr(struct net_device *, - struct e8390_pkt_hdr *, int); -static void block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void block_output(struct net_device *dev, int count, - const u_char *buf, const int start_page); - -static void axnet_detach(struct pcmcia_device *p_dev); - -static void AX88190_init(struct net_device *dev, int startp); -static int ax_open(struct net_device *dev); -static int ax_close(struct net_device *dev); -static irqreturn_t ax_interrupt(int irq, void *dev_id); - -/*====================================================================*/ - -struct axnet_dev { - struct pcmcia_device *p_dev; - caddr_t base; - struct timer_list watchdog; - int stale, fast_poll; - u_short link_status; - u_char duplex_flag; - int phy_id; - int flags; - int active_low; -}; - -static inline struct axnet_dev *PRIV(struct net_device *dev) -{ - void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device); - return p; -} - -static const struct net_device_ops axnet_netdev_ops = { - .ndo_open = axnet_open, - .ndo_stop = axnet_close, - .ndo_eth_ioctl = axnet_ioctl, - .ndo_start_xmit = axnet_start_xmit, - .ndo_tx_timeout = axnet_tx_timeout, - .ndo_get_stats = get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int axnet_probe(struct pcmcia_device *link) -{ - struct axnet_dev *info; - struct net_device *dev; - struct ei_device *ei_local; - - dev_dbg(&link->dev, "axnet_attach()\n"); - - dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(struct axnet_dev)); - if (!dev) - return -ENOMEM; - - ei_local = netdev_priv(dev); - spin_lock_init(&ei_local->page_lock); - - info = PRIV(dev); - info->p_dev = link; - link->priv = dev; - link->config_flags |= CONF_ENABLE_IRQ; - - dev->netdev_ops = &axnet_netdev_ops; - - dev->watchdog_timeo = TX_TIMEOUT; - - return axnet_config(link); -} /* axnet_attach */ - -static void axnet_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link); - - unregister_netdev(dev); - - axnet_release(link); - - free_netdev(dev); -} /* axnet_detach */ - -/*====================================================================== - - This probes for a card's hardware address by reading the PROM. - -======================================================================*/ - -static int get_prom(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - unsigned int ioaddr = dev->base_addr; - u8 addr[ETH_ALEN]; - int i, j; - - /* This is based on drivers/net/ethernet/8390/ne.c */ - struct { - u_char value, offset; - } program_seq[] = { - {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ - {0x01, EN0_DCFG}, /* Set word-wide access. */ - {0x00, EN0_RCNTLO}, /* Clear the count regs. */ - {0x00, EN0_RCNTHI}, - {0x00, EN0_IMR}, /* Mask completion irq. */ - {0xFF, EN0_ISR}, - {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */ - {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */ - {0x10, EN0_RCNTLO}, - {0x00, EN0_RCNTHI}, - {0x00, EN0_RSARLO}, /* DMA starting at 0x0400. */ - {0x04, EN0_RSARHI}, - {E8390_RREAD+E8390_START, E8390_CMD}, - }; - - /* Not much of a test, but the alternatives are messy */ - if (link->config_base != 0x03c0) - return 0; - - axnet_reset_8390(dev); - mdelay(10); - - for (i = 0; i < ARRAY_SIZE(program_seq); i++) - outb_p(program_seq[i].value, ioaddr + program_seq[i].offset); - - for (i = 0; i < 6; i += 2) { - j = inw(ioaddr + AXNET_DATAPORT); - addr[i] = j & 0xff; - addr[i+1] = j >> 8; - } - eth_hw_addr_set(dev, addr); - - return 1; -} /* get_prom */ - -static int try_io_port(struct pcmcia_device *link) -{ - int j, ret; - link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; - if (link->resource[0]->end == 32) { - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - /* for master/slave multifunction cards */ - if (link->resource[1]->end > 0) - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - } else { - /* This should be two 16-port windows */ - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16; - } - if (link->resource[0]->start == 0) { - for (j = 0; j < 0x400; j += 0x20) { - link->resource[0]->start = j ^ 0x300; - link->resource[1]->start = (j ^ 0x300) + 0x10; - link->io_lines = 16; - ret = pcmcia_request_io(link); - if (ret == 0) - return ret; - } - return ret; - } else { - return pcmcia_request_io(link); - } -} - -static int axnet_configcheck(struct pcmcia_device *p_dev, void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - p_dev->config_index = 0x05; - if (p_dev->resource[0]->end + p_dev->resource[1]->end < 32) - return -ENODEV; - - return try_io_port(p_dev); -} - -static int axnet_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct axnet_dev *info = PRIV(dev); - int i, j, j2, ret; - - dev_dbg(&link->dev, "axnet_config(0x%p)\n", link); - - /* don't trust the CIS on this; Linksys got it wrong */ - link->config_regs = 0x63; - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - ret = pcmcia_loop_config(link, axnet_configcheck, NULL); - if (ret != 0) - goto failed; - - if (!link->irq) - goto failed; - - if (resource_size(link->resource[1]) == 8) - link->config_flags |= CONF_ENABLE_SPKR; - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - if (!get_prom(link)) { - pr_notice("this is not an AX88190 card!\n"); - pr_notice("use pcnet_cs instead.\n"); - goto failed; - } - - ei_status.name = "AX88190"; - ei_status.word16 = 1; - ei_status.tx_start_page = AXNET_START_PG; - ei_status.rx_start_page = AXNET_START_PG + TX_PAGES; - ei_status.stop_page = AXNET_STOP_PG; - ei_status.reset_8390 = axnet_reset_8390; - ei_status.get_8390_hdr = get_8390_hdr; - ei_status.block_input = block_input; - ei_status.block_output = block_output; - - if (inb(dev->base_addr + AXNET_TEST) != 0) - info->flags |= IS_AX88790; - else - info->flags |= IS_AX88190; - - if (info->flags & IS_AX88790) - outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */ - - info->active_low = 0; - - for (i = 0; i < 32; i++) { - j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); - j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2); - if (j == j2) continue; - if ((j != 0) && (j != 0xffff)) break; - } - - if (i == 32) { - /* Maybe PHY is in power down mode. (PPD_SET = 1) - Bit 2 of CCSR is active low. */ - pcmcia_write_config_byte(link, CISREG_CCSR, 0x04); - for (i = 0; i < 32; i++) { - j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); - j2 = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 2); - if (j == j2) continue; - if ((j != 0) && (j != 0xffff)) { - info->active_low = 1; - break; - } - } - } - - info->phy_id = (i < 32) ? i : -1; - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "Asix AX88%d90: io %#3lx, irq %d, hw_addr %pM\n", - ((info->flags & IS_AX88790) ? 7 : 1), - dev->base_addr, dev->irq, dev->dev_addr); - if (info->phy_id != -1) { - netdev_dbg(dev, " MII transceiver at index %d, status %x\n", - info->phy_id, j); - } else { - netdev_notice(dev, " No MII transceivers found!\n"); - } - return 0; - -failed: - axnet_release(link); - return -ENODEV; -} /* axnet_config */ - -static void axnet_release(struct pcmcia_device *link) -{ - pcmcia_disable_device(link); -} - -static int axnet_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int axnet_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct axnet_dev *info = PRIV(dev); - - if (link->open) { - if (info->active_low == 1) - pcmcia_write_config_byte(link, CISREG_CCSR, 0x04); - - axnet_reset_8390(dev); - AX88190_init(dev, 1); - netif_device_attach(dev); - } - - return 0; -} - - -/*====================================================================== - - MII interface support - -======================================================================*/ - -#define MDIO_SHIFT_CLK 0x01 -#define MDIO_DATA_WRITE0 0x00 -#define MDIO_DATA_WRITE1 0x08 -#define MDIO_DATA_READ 0x04 -#define MDIO_MASK 0x0f -#define MDIO_ENB_IN 0x02 - -static void mdio_sync(unsigned int addr) -{ - int bits; - for (bits = 0; bits < 32; bits++) { - outb_p(MDIO_DATA_WRITE1, addr); - outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); - } -} - -static int mdio_read(unsigned int addr, int phy_id, int loc) -{ - u_int cmd = (0xf6<<10)|(phy_id<<5)|loc; - int i, retval = 0; - - mdio_sync(addr); - for (i = 14; i >= 0; i--) { - int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; - outb_p(dat, addr); - outb_p(dat | MDIO_SHIFT_CLK, addr); - } - for (i = 19; i > 0; i--) { - outb_p(MDIO_ENB_IN, addr); - retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0); - outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(unsigned int addr, int phy_id, int loc, int value) -{ - u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; - int i; - - mdio_sync(addr); - for (i = 31; i >= 0; i--) { - int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; - outb_p(dat, addr); - outb_p(dat | MDIO_SHIFT_CLK, addr); - } - for (i = 1; i >= 0; i--) { - outb_p(MDIO_ENB_IN, addr); - outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr); - } -} - -/*====================================================================*/ - -static int axnet_open(struct net_device *dev) -{ - int ret; - struct axnet_dev *info = PRIV(dev); - struct pcmcia_device *link = info->p_dev; - unsigned int nic_base = dev->base_addr; - - dev_dbg(&link->dev, "axnet_open('%s')\n", dev->name); - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - outb_p(0xFF, nic_base + EN0_ISR); /* Clear bogus intr. */ - ret = request_irq(dev->irq, ei_irq_wrapper, IRQF_SHARED, "axnet_cs", dev); - if (ret) - return ret; - - link->open++; - - info->link_status = 0x00; - timer_setup(&info->watchdog, ei_watchdog, 0); - mod_timer(&info->watchdog, jiffies + HZ); - - return ax_open(dev); -} /* axnet_open */ - -/*====================================================================*/ - -static int axnet_close(struct net_device *dev) -{ - struct axnet_dev *info = PRIV(dev); - struct pcmcia_device *link = info->p_dev; - - dev_dbg(&link->dev, "axnet_close('%s')\n", dev->name); - - ax_close(dev); - free_irq(dev->irq, dev); - - link->open--; - netif_stop_queue(dev); - timer_delete_sync(&info->watchdog); - - return 0; -} /* axnet_close */ - -/*====================================================================== - - Hard reset the card. This used to pause for the same period that - a 8390 reset command required, but that shouldn't be necessary. - -======================================================================*/ - -static void axnet_reset_8390(struct net_device *dev) -{ - unsigned int nic_base = dev->base_addr; - int i; - - ei_status.txing = ei_status.dmaing = 0; - - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD); - - outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET); - - for (i = 0; i < 100; i++) { - if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0) - break; - udelay(100); - } - outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */ - - if (i == 100) - netdev_err(dev, "axnet_reset_8390() did not complete\n"); - -} /* axnet_reset_8390 */ - -/*====================================================================*/ - -static irqreturn_t ei_irq_wrapper(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - PRIV(dev)->stale = 0; - return ax_interrupt(irq, dev_id); -} - -static void ei_watchdog(struct timer_list *t) -{ - struct axnet_dev *info = timer_container_of(info, t, watchdog); - struct net_device *dev = info->p_dev->priv; - unsigned int nic_base = dev->base_addr; - unsigned int mii_addr = nic_base + AXNET_MII_EEP; - u_short link; - - if (!netif_device_present(dev)) goto reschedule; - - /* Check for pending interrupt with expired latency timer: with - this, we can limp along even if the interrupt is blocked */ - if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) { - if (!info->fast_poll) - netdev_info(dev, "interrupt(s) dropped!\n"); - ei_irq_wrapper(dev->irq, dev); - info->fast_poll = HZ; - } - if (info->fast_poll) { - info->fast_poll--; - info->watchdog.expires = jiffies + 1; - add_timer(&info->watchdog); - return; - } - - if (info->phy_id < 0) - goto reschedule; - link = mdio_read(mii_addr, info->phy_id, 1); - if (!link || (link == 0xffff)) { - netdev_info(dev, "MII is missing!\n"); - info->phy_id = -1; - goto reschedule; - } - - link &= 0x0004; - if (link != info->link_status) { - u_short p = mdio_read(mii_addr, info->phy_id, 5); - netdev_info(dev, "%s link beat\n", link ? "found" : "lost"); - if (link) { - info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00; - if (p) - netdev_info(dev, "autonegotiation complete: %dbaseT-%cD selected\n", - (p & 0x0180) ? 100 : 10, (p & 0x0140) ? 'F' : 'H'); - else - netdev_info(dev, "link partner did not autonegotiate\n"); - AX88190_init(dev, 1); - } - info->link_status = link; - } - -reschedule: - info->watchdog.expires = jiffies + HZ; - add_timer(&info->watchdog); -} - -/*====================================================================*/ - -static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct axnet_dev *info = PRIV(dev); - struct mii_ioctl_data *data = if_mii(rq); - unsigned int mii_addr = dev->base_addr + AXNET_MII_EEP; - switch (cmd) { - case SIOCGMIIPHY: - data->phy_id = info->phy_id; - fallthrough; - case SIOCGMIIREG: /* Read MII PHY register. */ - data->val_out = mdio_read(mii_addr, data->phy_id, data->reg_num & 0x1f); - return 0; - case SIOCSMIIREG: /* Write MII PHY register. */ - mdio_write(mii_addr, data->phy_id, data->reg_num & 0x1f, data->val_in); - return 0; - } - return -EOPNOTSUPP; -} - -/*====================================================================*/ - -static void get_8390_hdr(struct net_device *dev, - struct e8390_pkt_hdr *hdr, - int ring_page) -{ - unsigned int nic_base = dev->base_addr; - - outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */ - outb_p(ring_page, nic_base + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD); - - insw(nic_base + AXNET_DATAPORT, hdr, - sizeof(struct e8390_pkt_hdr)>>1); - /* Fix for big endian systems */ - hdr->count = le16_to_cpu(hdr->count); - -} - -/*====================================================================*/ - -static void block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset) -{ - unsigned int nic_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - char *buf = skb->data; - - if ((netif_msg_rx_status(ei_local)) && (count != 4)) - netdev_dbg(dev, "[bi=%d]\n", count+4); - outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO); - outb_p(ring_offset >> 8, nic_base + EN0_RSARHI); - outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD); - - insw(nic_base + AXNET_DATAPORT,buf,count>>1); - if (count & 0x01) { - buf[count-1] = inb(nic_base + AXNET_DATAPORT); - } -} - -/*====================================================================*/ - -static void block_output(struct net_device *dev, int count, - const u_char *buf, const int start_page) -{ - unsigned int nic_base = dev->base_addr; - - pr_debug("%s: [bo=%d]\n", dev->name, count); - - /* Round the count up for word writes. Do we need to do this? - What effect will an odd byte count have on the 8390? - I should check someday. */ - if (count & 0x01) - count++; - - outb_p(0x00, nic_base + EN0_RSARLO); - outb_p(start_page, nic_base + EN0_RSARHI); - outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD); - outsw(nic_base + AXNET_DATAPORT, buf, count>>1); -} - -static const struct pcmcia_device_id axnet_ids[] = { - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081), - PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301), - PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328), - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301), - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303), - PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309), - PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106), - PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), - PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202), - PCMCIA_DEVICE_MANF_CARD(0xffff, 0x1090), - PCMCIA_DEVICE_PROD_ID12("AmbiCom,Inc.", "Fast Ethernet PC Card(AMB8110)", 0x49b020a7, 0x119cc9fc), - PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef), - PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef), - PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1), - PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc), - PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e), - PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXM", 0x5261440f, 0x3abbd061), - PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90), - PCMCIA_DEVICE_PROD_ID12("IO DATA", "ETXPCM", 0x547e66dc, 0x233adac2), - PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8), - PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609), - PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058), - PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6, 0xab9be5ef), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, axnet_ids); - -static struct pcmcia_driver axnet_cs_driver = { - .owner = THIS_MODULE, - .name = "axnet_cs", - .probe = axnet_probe, - .remove = axnet_detach, - .id_table = axnet_ids, - .suspend = axnet_suspend, - .resume = axnet_resume, -}; -module_pcmcia_driver(axnet_cs_driver); - -/*====================================================================*/ - -/* 8390.c: A general NS8390 ethernet driver core for linux. */ -/* - Written 1992-94 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This is the chip-specific code for many 8390-based ethernet adaptors. - This is not a complete driver, it must be combined with board-specific - code such as ne.c, wd.c, 3c503.c, etc. - - Seeing how at least eight drivers use this code, (not counting the - PCMCIA ones either) it is easy to break some card by what seems like - a simple innocent change. Please contact me or Donald if you think - you have found something that needs changing. -- PG - - Changelog: - - Paul Gortmaker : remove set_bit lock, other cleanups. - Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to - ei_block_input() for eth_io_copy_and_sum(). - Paul Gortmaker : exchange static int ei_pingpong for a #define, - also add better Tx error handling. - Paul Gortmaker : rewrite Rx overrun handling as per NS specs. - Alexey Kuznetsov : use the 8390's six bit hash multicast filter. - Paul Gortmaker : tweak ANK's above multicast changes a bit. - Paul Gortmaker : update packet statistics for v2.1.x - Alan Cox : support arbitrary stupid port mappings on the - 68K Macintosh. Support >16bit I/O spaces - Paul Gortmaker : add kmod support for auto-loading of the 8390 - module by all drivers that require it. - Alan Cox : Spinlocking work, added 'BUG_83C690' - Paul Gortmaker : Separate out Tx timeout code from Tx path. - - Sources: - The National Semiconductor LAN Databook, and the 3Com 3c503 databook. - - */ - -#include <linux/bitops.h> -#include <asm/irq.h> -#include <linux/fcntl.h> -#include <linux/in.h> -#include <linux/interrupt.h> - -#define BUG_83C690 - -/* These are the operational function interfaces to board-specific - routines. - void reset_8390(struct net_device *dev) - Resets the board associated with DEV, including a hardware reset of - the 8390. This is only called when there is a transmit timeout, and - it is always followed by 8390_init(). - void block_output(struct net_device *dev, int count, const unsigned char *buf, - int start_page) - Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The - "page" value uses the 8390's 256-byte pages. - void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page) - Read the 4 byte, page aligned 8390 header. *If* there is a - subsequent read, it will be of the rest of the packet. - void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) - Read COUNT bytes from the packet buffer into the skb data area. Start - reading from RING_OFFSET, the address as the 8390 sees it. This will always - follow the read of the 8390 header. -*/ -#define ei_reset_8390 (ei_local->reset_8390) -#define ei_block_output (ei_local->block_output) -#define ei_block_input (ei_local->block_input) -#define ei_get_8390_hdr (ei_local->get_8390_hdr) - -/* Index to functions. */ -static void ei_tx_intr(struct net_device *dev); -static void ei_tx_err(struct net_device *dev); -static void ei_receive(struct net_device *dev); -static void ei_rx_overrun(struct net_device *dev); - -/* Routines generic to NS8390-based boards. */ -static void NS8390_trigger_send(struct net_device *dev, unsigned int length, - int start_page); -static void do_set_multicast_list(struct net_device *dev); - -/* - * SMP and the 8390 setup. - * - * The 8390 isn't exactly designed to be multithreaded on RX/TX. There is - * a page register that controls bank and packet buffer access. We guard - * this with ei_local->page_lock. Nobody should assume or set the page other - * than zero when the lock is not held. Lock holders must restore page 0 - * before unlocking. Even pure readers must take the lock to protect in - * page 0. - * - * To make life difficult the chip can also be very slow. We therefore can't - * just use spinlocks. For the longer lockups we disable the irq the device - * sits on and hold the lock. We must hold the lock because there is a dual - * processor case other than interrupts (get stats/set multicast list in - * parallel with each other and transmit). - * - * Note: in theory we can just disable the irq on the card _but_ there is - * a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs" - * enter lock, take the queued irq. So we waddle instead of flying. - * - * Finally by special arrangement for the purpose of being generally - * annoying the transmit function is called bh atomic. That places - * restrictions on the user context callers as disable_irq won't save - * them. - */ - -/** - * ax_open - Open/initialize the board. - * @dev: network device to initialize - * - * This routine goes all-out, setting everything - * up anew at each open, even though many of these registers should only - * need to be set once at boot. - */ -static int ax_open(struct net_device *dev) -{ - unsigned long flags; - struct ei_device *ei_local = netdev_priv(dev); - - /* - * Grab the page lock so we own the register set, then call - * the init function. - */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - AX88190_init(dev, 1); - /* Set the flag before we drop the lock, That way the IRQ arrives - after its set and we get no silly warnings */ - netif_start_queue(dev); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - ei_local->irqlock = 0; - return 0; -} - -#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock) - -/** - * ax_close - shut down network device - * @dev: network device to close - * - * Opposite of ax_open(). Only used when "ifconfig <devname> down" is done. - */ -static int ax_close(struct net_device *dev) -{ - unsigned long flags; - - /* - * Hold the page lock during close - */ - - spin_lock_irqsave(&dev_lock(dev), flags); - AX88190_init(dev, 0); - spin_unlock_irqrestore(&dev_lock(dev), flags); - netif_stop_queue(dev); - return 0; -} - -/** - * axnet_tx_timeout - handle transmit time out condition - * @dev: network device which has apparently fallen asleep - * @txqueue: unused - * - * Called by kernel when device never acknowledges a transmit has - * completed (or failed) - i.e. never posted a Tx related interrupt. - */ - -static void axnet_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int txsr, isr, tickssofar = jiffies - dev_trans_start(dev); - unsigned long flags; - - dev->stats.tx_errors++; - - spin_lock_irqsave(&ei_local->page_lock, flags); - txsr = inb(e8390_base+EN0_TSR); - isr = inb(e8390_base+EN0_ISR); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - - netdev_dbg(dev, "Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n", - (txsr & ENTSR_ABT) ? "excess collisions." : - (isr) ? "lost interrupt?" : "cable problem?", - txsr, isr, tickssofar); - - if (!isr && !dev->stats.tx_packets) - { - /* The 8390 probably hasn't gotten on the cable yet. */ - ei_local->interface_num ^= 1; /* Try a different xcvr. */ - } - - /* Ugly but a reset can be slow, yet must be protected */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - - /* Try to restart the card. Perhaps the user has fixed something. */ - ei_reset_8390(dev); - AX88190_init(dev, 1); - - spin_unlock_irqrestore(&ei_local->page_lock, flags); - netif_wake_queue(dev); -} - -/** - * axnet_start_xmit - begin packet transmission - * @skb: packet to be sent - * @dev: network device to which packet is sent - * - * Sends a packet to an 8390 network device. - */ - -static netdev_tx_t axnet_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int length, send_length, output_page; - unsigned long flags; - u8 packet[ETH_ZLEN]; - - netif_stop_queue(dev); - - length = skb->len; - - /* Mask interrupts from the ethercard. - SMP: We have to grab the lock here otherwise the IRQ handler - on another CPU can flip window and race the IRQ mask set. We end - up trashing the mcast filter not disabling irqs if we don't lock */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - outb_p(0x00, e8390_base + EN0_IMR); - - /* - * Slow phase with lock held. - */ - - ei_local->irqlock = 1; - - send_length = max(length, ETH_ZLEN); - - /* - * We have two Tx slots available for use. Find the first free - * slot, and then perform some sanity checks. With two Tx bufs, - * you get very close to transmitting back-to-back packets. With - * only one Tx buf, the transmitter sits idle while you reload the - * card, leaving a substantial gap between each transmitted packet. - */ - - if (ei_local->tx1 == 0) - { - output_page = ei_local->tx_start_page; - ei_local->tx1 = send_length; - if ((netif_msg_tx_queued(ei_local)) && - ei_local->tx2 > 0) - netdev_dbg(dev, - "idle transmitter tx2=%d, lasttx=%d, txing=%d\n", - ei_local->tx2, ei_local->lasttx, - ei_local->txing); - } - else if (ei_local->tx2 == 0) - { - output_page = ei_local->tx_start_page + TX_PAGES/2; - ei_local->tx2 = send_length; - if ((netif_msg_tx_queued(ei_local)) && - ei_local->tx1 > 0) - netdev_dbg(dev, - "idle transmitter, tx1=%d, lasttx=%d, txing=%d\n", - ei_local->tx1, ei_local->lasttx, - ei_local->txing); - } - else - { /* We should never get here. */ - netif_dbg(ei_local, tx_err, dev, - "No Tx buffers free! tx1=%d tx2=%d last=%d\n", - ei_local->tx1, ei_local->tx2, - ei_local->lasttx); - ei_local->irqlock = 0; - netif_stop_queue(dev); - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - dev->stats.tx_errors++; - return NETDEV_TX_BUSY; - } - - /* - * Okay, now upload the packet and trigger a send if the transmitter - * isn't already sending. If it is busy, the interrupt handler will - * trigger the send later, upon receiving a Tx done interrupt. - */ - - if (length == skb->len) - ei_block_output(dev, length, skb->data, output_page); - else { - memset(packet, 0, ETH_ZLEN); - skb_copy_from_linear_data(skb, packet, skb->len); - ei_block_output(dev, length, packet, output_page); - } - - if (! ei_local->txing) - { - ei_local->txing = 1; - NS8390_trigger_send(dev, send_length, output_page); - netif_trans_update(dev); - if (output_page == ei_local->tx_start_page) - { - ei_local->tx1 = -1; - ei_local->lasttx = -1; - } - else - { - ei_local->tx2 = -1; - ei_local->lasttx = -2; - } - } - else ei_local->txqueue++; - - if (ei_local->tx1 && ei_local->tx2) - netif_stop_queue(dev); - else - netif_start_queue(dev); - - /* Turn 8390 interrupts back on. */ - ei_local->irqlock = 0; - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - - spin_unlock_irqrestore(&ei_local->page_lock, flags); - - dev_kfree_skb (skb); - dev->stats.tx_bytes += send_length; - - return NETDEV_TX_OK; -} - -/** - * ax_interrupt - handle the interrupts from an 8390 - * @irq: interrupt number - * @dev_id: a pointer to the net_device - * - * Handle the ether interface interrupts. We pull packets from - * the 8390 via the card specific functions and fire them at the networking - * stack. We also handle transmit completions and wake the transmit path if - * necessary. We also update the counters and do other housekeeping as - * needed. - */ - -static irqreturn_t ax_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - long e8390_base; - int interrupts, nr_serviced = 0, i; - struct ei_device *ei_local; - int handled = 0; - unsigned long flags; - - e8390_base = dev->base_addr; - ei_local = netdev_priv(dev); - - /* - * Protect the irq test too. - */ - - spin_lock_irqsave(&ei_local->page_lock, flags); - - if (ei_local->irqlock) { -#if 1 /* This might just be an interrupt for a PCI device sharing this line */ - const char *msg; - /* The "irqlock" check is only for testing. */ - if (ei_local->irqlock) - msg = "Interrupted while interrupts are masked!"; - else - msg = "Reentering the interrupt handler!"; - netdev_info(dev, "%s, isr=%#2x imr=%#2x\n", - msg, - inb_p(e8390_base + EN0_ISR), - inb_p(e8390_base + EN0_IMR)); -#endif - spin_unlock_irqrestore(&ei_local->page_lock, flags); - return IRQ_NONE; - } - - netif_dbg(ei_local, intr, dev, "interrupt(isr=%#2.2x)\n", - inb_p(e8390_base + EN0_ISR)); - - outb_p(0x00, e8390_base + EN0_ISR); - ei_local->irqlock = 1; - - /* !!Assumption!! -- we stay in page 0. Don't break this. */ - while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 && - ++nr_serviced < MAX_SERVICE) - { - if (!netif_running(dev) || (interrupts == 0xff)) { - netif_warn(ei_local, intr, dev, - "interrupt from stopped card\n"); - outb_p(interrupts, e8390_base + EN0_ISR); - interrupts = 0; - break; - } - handled = 1; - - /* AX88190 bug fix. */ - outb_p(interrupts, e8390_base + EN0_ISR); - for (i = 0; i < 10; i++) { - if (!(inb(e8390_base + EN0_ISR) & interrupts)) - break; - outb_p(0, e8390_base + EN0_ISR); - outb_p(interrupts, e8390_base + EN0_ISR); - } - if (interrupts & ENISR_OVER) - ei_rx_overrun(dev); - else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) - { - /* Got a good (?) packet. */ - ei_receive(dev); - } - /* Push the next to-transmit packet through. */ - if (interrupts & ENISR_TX) - ei_tx_intr(dev); - else if (interrupts & ENISR_TX_ERR) - ei_tx_err(dev); - - if (interrupts & ENISR_COUNTERS) - { - dev->stats.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0); - dev->stats.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1); - dev->stats.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2); - } - } - - if (interrupts && (netif_msg_intr(ei_local))) - { - handled = 1; - if (nr_serviced >= MAX_SERVICE) - { - /* 0xFF is valid for a card removal */ - if (interrupts != 0xFF) - netdev_warn(dev, - "Too much work at interrupt, status %#2.2x\n", - interrupts); - outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */ - } else { - netdev_warn(dev, "unknown interrupt %#2x\n", - interrupts); - outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */ - } - } - - /* Turn 8390 interrupts back on. */ - ei_local->irqlock = 0; - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - - spin_unlock_irqrestore(&ei_local->page_lock, flags); - return IRQ_RETVAL(handled); -} - -/** - * ei_tx_err - handle transmitter error - * @dev: network device which threw the exception - * - * A transmitter error has happened. Most likely excess collisions (which - * is a fairly normal condition). If the error is one where the Tx will - * have been aborted, we try and send another one right away, instead of - * letting the failed packet sit and collect dust in the Tx buffer. This - * is a much better solution as it avoids kernel based Tx timeouts, and - * an unnecessary card reset. - * - * Called with lock held. - */ - -static void ei_tx_err(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - unsigned char txsr = inb_p(e8390_base+EN0_TSR); - unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU); - -#ifdef VERBOSE_ERROR_DUMP - netdev_dbg(dev, "transmitter error (%#2x):", txsr); - if (txsr & ENTSR_ABT) - pr_cont(" excess-collisions"); - if (txsr & ENTSR_ND) - pr_cont(" non-deferral"); - if (txsr & ENTSR_CRS) - pr_cont(" lost-carrier"); - if (txsr & ENTSR_FU) - pr_cont(" FIFO-underrun"); - if (txsr & ENTSR_CDH) - pr_cont(" lost-heartbeat"); - pr_cont("\n"); -#endif - - if (tx_was_aborted) - ei_tx_intr(dev); - else - { - dev->stats.tx_errors++; - if (txsr & ENTSR_CRS) dev->stats.tx_carrier_errors++; - if (txsr & ENTSR_CDH) dev->stats.tx_heartbeat_errors++; - if (txsr & ENTSR_OWC) dev->stats.tx_window_errors++; - } -} - -/** - * ei_tx_intr - transmit interrupt handler - * @dev: network device for which tx intr is handled - * - * We have finished a transmit: check for errors and then trigger the next - * packet to be sent. Called with lock held. - */ - -static void ei_tx_intr(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int status = inb(e8390_base + EN0_TSR); - - /* - * There are two Tx buffers, see which one finished, and trigger - * the send of another one if it exists. - */ - ei_local->txqueue--; - - if (ei_local->tx1 < 0) - { - if (ei_local->lasttx != 1 && ei_local->lasttx != -1) - netdev_err(dev, "%s: bogus last_tx_buffer %d, tx1=%d\n", - ei_local->name, ei_local->lasttx, - ei_local->tx1); - ei_local->tx1 = 0; - if (ei_local->tx2 > 0) - { - ei_local->txing = 1; - NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); - netif_trans_update(dev); - ei_local->tx2 = -1; - ei_local->lasttx = 2; - } else { - ei_local->lasttx = 20; - ei_local->txing = 0; - } - } - else if (ei_local->tx2 < 0) - { - if (ei_local->lasttx != 2 && ei_local->lasttx != -2) - netdev_err(dev, "%s: bogus last_tx_buffer %d, tx2=%d\n", - ei_local->name, ei_local->lasttx, - ei_local->tx2); - ei_local->tx2 = 0; - if (ei_local->tx1 > 0) - { - ei_local->txing = 1; - NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); - netif_trans_update(dev); - ei_local->tx1 = -1; - ei_local->lasttx = 1; - } else { - ei_local->lasttx = 10; - ei_local->txing = 0; - } - } -// else -// netdev_warn(dev, "unexpected TX-done interrupt, lasttx=%d\n", -// ei_local->lasttx); - - /* Minimize Tx latency: update the statistics after we restart TXing. */ - if (status & ENTSR_COL) - dev->stats.collisions++; - if (status & ENTSR_PTX) - dev->stats.tx_packets++; - else - { - dev->stats.tx_errors++; - if (status & ENTSR_ABT) - { - dev->stats.tx_aborted_errors++; - dev->stats.collisions += 16; - } - if (status & ENTSR_CRS) - dev->stats.tx_carrier_errors++; - if (status & ENTSR_FU) - dev->stats.tx_fifo_errors++; - if (status & ENTSR_CDH) - dev->stats.tx_heartbeat_errors++; - if (status & ENTSR_OWC) - dev->stats.tx_window_errors++; - } - netif_wake_queue(dev); -} - -/** - * ei_receive - receive some packets - * @dev: network device with which receive will be run - * - * We have a good packet(s), get it/them out of the buffers. - * Called with lock held. - */ - -static void ei_receive(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - unsigned char rxing_page, this_frame, next_frame; - unsigned short current_offset; - int rx_pkt_count = 0; - struct e8390_pkt_hdr rx_frame; - - while (++rx_pkt_count < 10) - { - int pkt_len, pkt_stat; - - /* Get the rx page (incoming packet pointer). */ - rxing_page = inb_p(e8390_base + EN1_CURPAG -1); - - /* Remove one frame from the ring. Boundary is always a page behind. */ - this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1; - if (this_frame >= ei_local->stop_page) - this_frame = ei_local->rx_start_page; - - /* Someday we'll omit the previous, iff we never get this message. - (There is at least one clone claimed to have a problem.) - - Keep quiet if it looks like a card removal. One problem here - is that some clones crash in roughly the same way. - */ - if ((netif_msg_rx_err(ei_local)) && - this_frame != ei_local->current_page && - (this_frame != 0x0 || rxing_page != 0xFF)) - netdev_err(dev, "mismatched read page pointers %2x vs %2x\n", - this_frame, ei_local->current_page); - - if (this_frame == rxing_page) /* Read all the frames? */ - break; /* Done for now */ - - current_offset = this_frame << 8; - ei_get_8390_hdr(dev, &rx_frame, this_frame); - - pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr); - pkt_stat = rx_frame.status; - - next_frame = this_frame + 1 + ((pkt_len+4)>>8); - - if (pkt_len < 60 || pkt_len > 1518) - { - netif_err(ei_local, rx_err, dev, - "bogus packet size: %d, status=%#2x nxpg=%#2x\n", - rx_frame.count, rx_frame.status, - rx_frame.next); - dev->stats.rx_errors++; - dev->stats.rx_length_errors++; - } - else if ((pkt_stat & 0x0F) == ENRSR_RXOK) - { - struct sk_buff *skb; - - skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) - { - netif_err(ei_local, rx_err, dev, - "Couldn't allocate a sk_buff of size %d\n", - pkt_len); - dev->stats.rx_dropped++; - break; - } - else - { - skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ - skb_put(skb, pkt_len); /* Make room */ - ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - if (pkt_stat & ENRSR_PHY) - dev->stats.multicast++; - } - } - else - { - netif_err(ei_local, rx_err, dev, - "bogus packet: status=%#2x nxpg=%#2x size=%d\n", - rx_frame.status, rx_frame.next, - rx_frame.count); - dev->stats.rx_errors++; - /* NB: The NIC counts CRC, frame and missed errors. */ - if (pkt_stat & ENRSR_FO) - dev->stats.rx_fifo_errors++; - } - next_frame = rx_frame.next; - - /* This _should_ never happen: it's here for avoiding bad clones. */ - if (next_frame >= ei_local->stop_page) { - netdev_info(dev, "next frame inconsistency, %#2x\n", - next_frame); - next_frame = ei_local->rx_start_page; - } - ei_local->current_page = next_frame; - outb_p(next_frame-1, e8390_base+EN0_BOUNDARY); - } -} - -/** - * ei_rx_overrun - handle receiver overrun - * @dev: network device which threw exception - * - * We have a receiver overrun: we have to kick the 8390 to get it started - * again. Problem is that you have to kick it exactly as NS prescribes in - * the updated datasheets, or "the NIC may act in an unpredictable manner." - * This includes causing "the NIC to defer indefinitely when it is stopped - * on a busy network." Ugh. - * Called with lock held. Don't call this with the interrupts off or your - * computer will hate you - it takes 10ms or so. - */ - -static void ei_rx_overrun(struct net_device *dev) -{ - struct axnet_dev *info = PRIV(dev); - long e8390_base = dev->base_addr; - unsigned char was_txing, must_resend = 0; - struct ei_device *ei_local = netdev_priv(dev); - - /* - * Record whether a Tx was in progress and then issue the - * stop command. - */ - was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS; - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); - - netif_dbg(ei_local, rx_err, dev, "Receiver overrun\n"); - dev->stats.rx_over_errors++; - - /* - * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. - * We wait at least 2ms. - */ - - mdelay(2); - - /* - * Reset RBCR[01] back to zero as per magic incantation. - */ - outb_p(0x00, e8390_base+EN0_RCNTLO); - outb_p(0x00, e8390_base+EN0_RCNTHI); - - /* - * See if any Tx was interrupted or not. According to NS, this - * step is vital, and skipping it will cause no end of havoc. - */ - - if (was_txing) - { - unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR); - if (!tx_completed) - must_resend = 1; - } - - /* - * Have to enter loopback mode and then restart the NIC before - * you are allowed to slurp packets up off the ring. - */ - outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); - outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); - - /* - * Clear the Rx ring of all the debris, and ack the interrupt. - */ - ei_receive(dev); - - /* - * Leave loopback mode, and resend any packet that got stopped. - */ - outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR); - if (must_resend) - outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD); -} - -/* - * Collect the stats. This is called unlocked and from several contexts. - */ - -static struct net_device_stats *get_stats(struct net_device *dev) -{ - long ioaddr = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - unsigned long flags; - - /* If the card is stopped, just return the present stats. */ - if (!netif_running(dev)) - return &dev->stats; - - spin_lock_irqsave(&ei_local->page_lock,flags); - /* Read the counter registers, assuming we are in page 0. */ - dev->stats.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0); - dev->stats.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1); - dev->stats.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2); - spin_unlock_irqrestore(&ei_local->page_lock, flags); - - return &dev->stats; -} - -/* - * Form the 64 bit 8390 multicast table from the linked list of addresses - * associated with this dev structure. - */ - -static inline void make_mc_bits(u8 *bits, struct net_device *dev) -{ - struct netdev_hw_addr *ha; - u32 crc; - - netdev_for_each_mc_addr(ha, dev) { - crc = ether_crc(ETH_ALEN, ha->addr); - /* - * The 8390 uses the 6 most significant bits of the - * CRC to index the multicast table. - */ - bits[crc>>29] |= (1<<((crc>>26)&7)); - } -} - -/** - * do_set_multicast_list - set/clear multicast filter - * @dev: net device for which multicast filter is adjusted - * - * Set or clear the multicast filter for this adaptor. - * Must be called with lock held. - */ - -static void do_set_multicast_list(struct net_device *dev) -{ - long e8390_base = dev->base_addr; - int i; - struct ei_device *ei_local = netdev_priv(dev); - - if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) { - memset(ei_local->mcfilter, 0, 8); - if (!netdev_mc_empty(dev)) - make_mc_bits(ei_local->mcfilter, dev); - } else { - /* set to accept-all */ - memset(ei_local->mcfilter, 0xFF, 8); - } - - outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD); - for(i = 0; i < 8; i++) - { - outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i)); - } - outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD); - - if(dev->flags&IFF_PROMISC) - outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR); - else if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) - outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR); - else - outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); - - outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); -} - -/* - * Called without lock held. This is invoked from user context and may - * be parallel to just about everything else. Its also fairly quick and - * not called too often. Must protect against both bh and irq users - */ - -static void set_multicast_list(struct net_device *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev_lock(dev), flags); - do_set_multicast_list(dev); - spin_unlock_irqrestore(&dev_lock(dev), flags); -} - -/* This page of functions should be 8390 generic */ -/* Follow National Semi's recommendations for initializing the "NIC". */ - -/** - * AX88190_init - initialize 8390 hardware - * @dev: network device to initialize - * @startp: boolean. non-zero value to initiate chip processing - * - * Must be called with lock held. - */ - -static void AX88190_init(struct net_device *dev, int startp) -{ - struct axnet_dev *info = PRIV(dev); - long e8390_base = dev->base_addr; - struct ei_device *ei_local = netdev_priv(dev); - int i; - int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48; - - if(sizeof(struct e8390_pkt_hdr)!=4) - panic("8390.c: header struct mispacked\n"); - /* Follow National Semi's recommendations for initing the DP83902. */ - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */ - outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */ - /* Clear the remote byte count registers. */ - outb_p(0x00, e8390_base + EN0_RCNTLO); - outb_p(0x00, e8390_base + EN0_RCNTHI); - /* Set to monitor and loopback mode -- this is vital!. */ - outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */ - outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */ - /* Set the transmit page and receive ring. */ - outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR); - ei_local->tx1 = ei_local->tx2 = 0; - outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG); - outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/ - ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */ - outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG); - /* Clear the pending interrupts and mask. */ - outb_p(0xFF, e8390_base + EN0_ISR); - outb_p(0x00, e8390_base + EN0_IMR); - - /* Copy the station address into the DS8390 registers. */ - - outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */ - for(i = 0; i < 6; i++) - { - outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i)); - if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) - netdev_err(dev, "Hw. address read/write mismap %d\n", i); - } - - outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG); - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); - - netif_start_queue(dev); - ei_local->tx1 = ei_local->tx2 = 0; - ei_local->txing = 0; - - if (info->flags & IS_AX88790) /* select Internal PHY */ - outb(0x10, e8390_base + AXNET_GPIO); - - if (startp) - { - outb_p(0xff, e8390_base + EN0_ISR); - outb_p(ENISR_ALL, e8390_base + EN0_IMR); - outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD); - outb_p(E8390_TXCONFIG | info->duplex_flag, - e8390_base + EN0_TXCR); /* xmit on. */ - /* 3c503 TechMan says rxconfig only after the NIC is started. */ - outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */ - do_set_multicast_list(dev); /* (re)load the mcast table */ - } -} - -/* Trigger a transmit start, assuming the length is valid. - Always called with the page lock held */ - -static void NS8390_trigger_send(struct net_device *dev, unsigned int length, - int start_page) -{ - long e8390_base = dev->base_addr; - struct ei_device *ei_local __attribute((unused)) = netdev_priv(dev); - - if (inb_p(e8390_base) & E8390_TRANS) - { - netdev_warn(dev, "trigger_send() called with the transmitter busy\n"); - return; - } - outb_p(length & 0xff, e8390_base + EN0_TCNTLO); - outb_p(length >> 8, e8390_base + EN0_TCNTHI); - outb_p(start_page, e8390_base + EN0_TPSR); - outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD); -} diff --git a/drivers/net/ethernet/8390/smc-ultra.c b/drivers/net/ethernet/8390/smc-ultra.c deleted file mode 100644 index 22ca804b2e95..000000000000 --- a/drivers/net/ethernet/8390/smc-ultra.c +++ /dev/null @@ -1,630 +0,0 @@ -// SPDX-License-Identifier: GPL-1.0+ -/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */ -/* - This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards. - - Written 1993-1998 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This driver uses the cards in the 8390-compatible mode. - Most of the run-time complexity is handled by the generic code in - 8390.c. The code in this file is responsible for - - ultra_probe() Detecting and initializing the card. - ultra_probe1() - ultra_probe_isapnp() - - ultra_open() The card-specific details of starting, stopping - ultra_reset_8390() and resetting the 8390 NIC core. - ultra_close() - - ultra_block_input() Routines for reading and writing blocks of - ultra_block_output() packet buffer memory. - ultra_pio_input() - ultra_pio_output() - - This driver enables the shared memory only when doing the actual data - transfers to avoid a bug in early version of the card that corrupted - data transferred by a AHA1542. - - This driver now supports the programmed-I/O (PIO) data transfer mode of - the EtherEZ. It does not use the non-8390-compatible "Altego" mode. - That support (if available) is in smc-ez.c. - - Changelog: - - Paul Gortmaker : multiple card support for module users. - Donald Becker : 4/17/96 PIO support, minor potential problems avoided. - Donald Becker : 6/6/96 correctly set auto-wrap bit. - Alexander Sotirov : 1/20/01 Added support for ISAPnP cards - - Note about the ISA PnP support: - - This driver can not autoprobe for more than one SMC EtherEZ PnP card. - You have to configure the second card manually through the /proc/isapnp - interface and then load the module with an explicit io=0x___ option. -*/ - -static const char version[] = - "smc-ultra.c:v2.02 2/3/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/isapnp.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <net/Space.h> - -#include <asm/io.h> -#include <asm/irq.h> - -#include "8390.h" - -#define DRV_NAME "smc-ultra" - -/* A zero-terminated list of I/O addresses to be probed. */ -static unsigned int ultra_portlist[] __initdata = -{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0}; - -static int ultra_probe1(struct net_device *dev, int ioaddr); - -#ifdef __ISAPNP__ -static int ultra_probe_isapnp(struct net_device *dev); -#endif - -static int ultra_open(struct net_device *dev); -static void ultra_reset_8390(struct net_device *dev); -static void ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page); -static void ultra_block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void ultra_block_output(struct net_device *dev, int count, - const unsigned char *buf, const int start_page); -static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page); -static void ultra_pio_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void ultra_pio_output(struct net_device *dev, int count, - const unsigned char *buf, const int start_page); -static int ultra_close_card(struct net_device *dev); - -#ifdef __ISAPNP__ -static struct isapnp_device_id ultra_device_ids[] __initdata = { - { ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), - ISAPNP_VENDOR('S','M','C'), ISAPNP_FUNCTION(0x8416), - (long) "SMC EtherEZ (8416)" }, - { } /* terminate list */ -}; - -MODULE_DEVICE_TABLE(isapnp, ultra_device_ids); -#endif - -static u32 ultra_msg_enable; - -#define START_PG 0x00 /* First page of TX buffer */ - -#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */ -#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */ -#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */ -#define IOPD 0x02 /* I/O Pipe Data (16 bits), PIO operation. */ -#define IOPA 0x07 /* I/O Pipe Address for PIO operation. */ -#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */ -#define ULTRA_IO_EXTENT 32 -#define EN0_ERWCNT 0x08 /* Early receive warning count. */ - -#ifdef CONFIG_NET_POLL_CONTROLLER -static void ultra_poll(struct net_device *dev) -{ - disable_irq(dev->irq); - ei_interrupt(dev->irq, dev); - enable_irq(dev->irq); -} -#endif -/* Probe for the Ultra. This looks like a 8013 with the station - address PROM at I/O ports <base>+8 to <base>+13, with a checksum - following. -*/ - -static int __init do_ultra_probe(struct net_device *dev) -{ - int i; - int base_addr = dev->base_addr; - int irq = dev->irq; - - if (base_addr > 0x1ff) /* Check a single specified location. */ - return ultra_probe1(dev, base_addr); - else if (base_addr != 0) /* Don't probe at all. */ - return -ENXIO; - -#ifdef __ISAPNP__ - /* Look for any installed ISAPnP cards */ - if (isapnp_present() && (ultra_probe_isapnp(dev) == 0)) - return 0; -#endif - - for (i = 0; ultra_portlist[i]; i++) { - dev->irq = irq; - if (ultra_probe1(dev, ultra_portlist[i]) == 0) - return 0; - } - - return -ENODEV; -} - -#ifndef MODULE -struct net_device * __init ultra_probe(int unit) -{ - struct net_device *dev = alloc_ei_netdev(); - int err; - - if (!dev) - return ERR_PTR(-ENOMEM); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - err = do_ultra_probe(dev); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); -} -#endif - -static const struct net_device_ops ultra_netdev_ops = { - .ndo_open = ultra_open, - .ndo_stop = ultra_close_card, - - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_rx_mode = ei_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ultra_poll, -#endif -}; - -static int __init ultra_probe1(struct net_device *dev, int ioaddr) -{ - int i, retval; - int checksum = 0; - u8 macaddr[ETH_ALEN]; - const char *model_name; - unsigned char eeprom_irq = 0; - static unsigned version_printed; - /* Values from various config regs. */ - unsigned char num_pages, irqreg, addr, piomode; - unsigned char idreg = inb(ioaddr + 7); - unsigned char reg4 = inb(ioaddr + 4) & 0x7f; - struct ei_device *ei_local = netdev_priv(dev); - - if (!request_region(ioaddr, ULTRA_IO_EXTENT, DRV_NAME)) - return -EBUSY; - - /* Check the ID nibble. */ - if ((idreg & 0xF0) != 0x20 /* SMC Ultra */ - && (idreg & 0xF0) != 0x40) { /* SMC EtherEZ */ - retval = -ENODEV; - goto out; - } - - /* Select the station address register set. */ - outb(reg4, ioaddr + 4); - - for (i = 0; i < 8; i++) - checksum += inb(ioaddr + 8 + i); - if ((checksum & 0xff) != 0xFF) { - retval = -ENODEV; - goto out; - } - - if ((ultra_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) - netdev_info(dev, version); - - model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ"; - - for (i = 0; i < 6; i++) - macaddr[i] = inb(ioaddr + 8 + i); - eth_hw_addr_set(dev, macaddr); - - netdev_info(dev, "%s at %#3x, %pM", model_name, - ioaddr, dev->dev_addr); - - /* Switch from the station address to the alternate register set and - read the useful registers there. */ - outb(0x80 | reg4, ioaddr + 4); - - /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */ - outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c); - piomode = inb(ioaddr + 0x8); - addr = inb(ioaddr + 0xb); - irqreg = inb(ioaddr + 0xd); - - /* Switch back to the station address register set so that the MS-DOS driver - can find the card after a warm boot. */ - outb(reg4, ioaddr + 4); - - if (dev->irq < 2) { - unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15}; - int irq; - - /* The IRQ bits are split. */ - irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)]; - - if (irq == 0) { - pr_cont(", failed to detect IRQ line.\n"); - retval = -EAGAIN; - goto out; - } - dev->irq = irq; - eeprom_irq = 1; - } - - /* The 8390 isn't at the base address, so fake the offset */ - dev->base_addr = ioaddr+ULTRA_NIC_OFFSET; - - { - static const int addr_tbl[4] = { - 0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000 - }; - static const short num_pages_tbl[4] = { - 0x20, 0x40, 0x80, 0xff - }; - - dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ; - num_pages = num_pages_tbl[(addr >> 4) & 3]; - } - - ei_status.name = model_name; - ei_status.word16 = 1; - ei_status.tx_start_page = START_PG; - ei_status.rx_start_page = START_PG + TX_PAGES; - ei_status.stop_page = num_pages; - - ei_status.mem = ioremap(dev->mem_start, (ei_status.stop_page - START_PG)*256); - if (!ei_status.mem) { - pr_cont(", failed to ioremap.\n"); - retval = -ENOMEM; - goto out; - } - - dev->mem_end = dev->mem_start + (ei_status.stop_page - START_PG)*256; - - if (piomode) { - pr_cont(", %s IRQ %d programmed-I/O mode.\n", - eeprom_irq ? "EEPROM" : "assigned ", dev->irq); - ei_status.block_input = &ultra_pio_input; - ei_status.block_output = &ultra_pio_output; - ei_status.get_8390_hdr = &ultra_pio_get_hdr; - } else { - pr_cont(", %s IRQ %d memory %#lx-%#lx.\n", - eeprom_irq ? "" : "assigned ", dev->irq, dev->mem_start, - dev->mem_end-1); - ei_status.block_input = &ultra_block_input; - ei_status.block_output = &ultra_block_output; - ei_status.get_8390_hdr = &ultra_get_8390_hdr; - } - ei_status.reset_8390 = &ultra_reset_8390; - - dev->netdev_ops = &ultra_netdev_ops; - NS8390_init(dev, 0); - ei_local->msg_enable = ultra_msg_enable; - - retval = register_netdev(dev); - if (retval) - goto out; - return 0; -out: - release_region(ioaddr, ULTRA_IO_EXTENT); - return retval; -} - -#ifdef __ISAPNP__ -static int __init ultra_probe_isapnp(struct net_device *dev) -{ - int i; - - for (i = 0; ultra_device_ids[i].vendor != 0; i++) { - struct pnp_dev *idev = NULL; - - while ((idev = pnp_find_dev(NULL, - ultra_device_ids[i].vendor, - ultra_device_ids[i].function, - idev))) { - /* Avoid already found cards from previous calls */ - if (pnp_device_attach(idev) < 0) - continue; - if (pnp_activate_dev(idev) < 0) { - __again: - pnp_device_detach(idev); - continue; - } - /* if no io and irq, search for next */ - if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) - goto __again; - /* found it */ - dev->base_addr = pnp_port_start(idev, 0); - dev->irq = pnp_irq(idev, 0); - netdev_info(dev, - "smc-ultra.c: ISAPnP reports %s at i/o %#lx, irq %d.\n", - (char *) ultra_device_ids[i].driver_data, - dev->base_addr, dev->irq); - if (ultra_probe1(dev, dev->base_addr) != 0) { /* Shouldn't happen. */ - netdev_err(dev, - "smc-ultra.c: Probe of ISAPnP card at %#lx failed.\n", - dev->base_addr); - pnp_device_detach(idev); - return -ENXIO; - } - ei_status.priv = (unsigned long)idev; - break; - } - if (!idev) - continue; - return 0; - } - - return -ENODEV; -} -#endif - -static int -ultra_open(struct net_device *dev) -{ - int retval; - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - unsigned char irq2reg[] = {0, 0, 0x04, 0x08, 0, 0x0C, 0, 0x40, - 0, 0x04, 0x44, 0x48, 0, 0, 0, 0x4C, }; - - retval = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); - if (retval) - return retval; - - outb(0x00, ioaddr); /* Disable shared memory for safety. */ - outb(0x80, ioaddr + 5); - /* Set the IRQ line. */ - outb(inb(ioaddr + 4) | 0x80, ioaddr + 4); - outb((inb(ioaddr + 13) & ~0x4C) | irq2reg[dev->irq], ioaddr + 13); - outb(inb(ioaddr + 4) & 0x7f, ioaddr + 4); - - if (ei_status.block_input == &ultra_pio_input) { - outb(0x11, ioaddr + 6); /* Enable interrupts and PIO. */ - outb(0x01, ioaddr + 0x19); /* Enable ring read auto-wrap. */ - } else - outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */ - /* Set the early receive warning level in window 0 high enough not - to receive ERW interrupts. */ - outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr); - outb(0xff, dev->base_addr + EN0_ERWCNT); - ei_open(dev); - return 0; -} - -static void -ultra_reset_8390(struct net_device *dev) -{ - int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */ - struct ei_device *ei_local = netdev_priv(dev); - - outb(ULTRA_RESET, cmd_port); - netif_dbg(ei_local, hw, dev, "resetting Ultra, t=%ld...\n", jiffies); - ei_status.txing = 0; - - outb(0x00, cmd_port); /* Disable shared memory for safety. */ - outb(0x80, cmd_port + 5); - if (ei_status.block_input == &ultra_pio_input) - outb(0x11, cmd_port + 6); /* Enable interrupts and PIO. */ - else - outb(0x01, cmd_port + 6); /* Enable interrupts and memory. */ - - netif_dbg(ei_local, hw, dev, "reset done\n"); -} - -/* Grab the 8390 specific header. Similar to the block_input routine, but - we don't need to be concerned with ring wrap as the header will be at - the start of a page, so we optimize accordingly. */ - -static void -ultra_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) -{ - void __iomem *hdr_start = ei_status.mem + ((ring_page - START_PG)<<8); - - outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */ -#ifdef __BIG_ENDIAN - /* Officially this is what we are doing, but the readl() is faster */ - /* unfortunately it isn't endian aware of the struct */ - memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); - hdr->count = le16_to_cpu(hdr->count); -#else - ((unsigned int*)hdr)[0] = readl(hdr_start); -#endif - outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */ -} - -/* Block input and output are easy on shared memory ethercards, the only - complication is when the ring buffer wraps. */ - -static void -ultra_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) -{ - void __iomem *xfer_start = ei_status.mem + ring_offset - (START_PG<<8); - - /* Enable shared memory. */ - outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); - - if (ring_offset + count > ei_status.stop_page*256) { - /* We must wrap the input move. */ - int semi_count = ei_status.stop_page*256 - ring_offset; - memcpy_fromio(skb->data, xfer_start, semi_count); - count -= semi_count; - memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); - } else { - memcpy_fromio(skb->data, xfer_start, count); - } - - outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ -} - -static void -ultra_block_output(struct net_device *dev, int count, const unsigned char *buf, - int start_page) -{ - void __iomem *shmem = ei_status.mem + ((start_page - START_PG)<<8); - - /* Enable shared memory. */ - outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); - - memcpy_toio(shmem, buf, count); - - outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */ -} - -/* The identical operations for programmed I/O cards. - The PIO model is trivial to use: the 16 bit start address is written - byte-sequentially to IOPA, with no intervening I/O operations, and the - data is read or written to the IOPD data port. - The only potential complication is that the address register is shared - and must be always be rewritten between each read/write direction change. - This is no problem for us, as the 8390 code ensures that we are single - threaded. */ -static void ultra_pio_get_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ - outb(ring_page, ioaddr + IOPA); - insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1); -} - -static void ultra_pio_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - char *buf = skb->data; - - /* For now set the address again, although it should already be correct. */ - outb(ring_offset, ioaddr + IOPA); /* Set the address, LSB first. */ - outb(ring_offset >> 8, ioaddr + IOPA); - /* We know skbuffs are padded to at least word alignment. */ - insw(ioaddr + IOPD, buf, (count+1)>>1); -} -static void ultra_pio_output(struct net_device *dev, int count, - const unsigned char *buf, const int start_page) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */ - outb(0x00, ioaddr + IOPA); /* Set the address, LSB first. */ - outb(start_page, ioaddr + IOPA); - /* An extra odd byte is OK here as well. */ - outsw(ioaddr + IOPD, buf, (count+1)>>1); -} - -static int -ultra_close_card(struct net_device *dev) -{ - int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */ - struct ei_device *ei_local = netdev_priv(dev); - - netif_stop_queue(dev); - - netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); - - outb(0x00, ioaddr + 6); /* Disable interrupts. */ - free_irq(dev->irq, dev); - - NS8390_init(dev, 0); - - /* We should someday disable shared memory and change to 8-bit mode - "just in case"... */ - - return 0; -} - - -#ifdef MODULE -#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */ -static struct net_device *dev_ultra[MAX_ULTRA_CARDS]; -static int io[MAX_ULTRA_CARDS]; -static int irq[MAX_ULTRA_CARDS]; - -module_param_hw_array(io, int, ioport, NULL, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param_named(msg_enable, ultra_msg_enable, uint, 0444); -MODULE_PARM_DESC(io, "I/O base address(es)"); -MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)"); -MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); -MODULE_DESCRIPTION("SMC Ultra/EtherEZ ISA/PnP Ethernet driver"); -MODULE_LICENSE("GPL"); - -/* This is set up so that only a single autoprobe takes place per call. -ISA device autoprobes on a running machine are not recommended. */ -static int __init ultra_init_module(void) -{ - struct net_device *dev; - int this_dev, found = 0; - - for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { - if (io[this_dev] == 0) { - if (this_dev != 0) break; /* only autoprobe 1st one */ - printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n"); - } - dev = alloc_ei_netdev(); - if (!dev) - break; - dev->irq = irq[this_dev]; - dev->base_addr = io[this_dev]; - if (do_ultra_probe(dev) == 0) { - dev_ultra[found++] = dev; - continue; - } - free_netdev(dev); - printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]); - break; - } - if (found) - return 0; - return -ENXIO; -} -module_init(ultra_init_module); - -static void cleanup_card(struct net_device *dev) -{ - /* NB: ultra_close_card() does free_irq */ -#ifdef __ISAPNP__ - struct pnp_dev *idev = (struct pnp_dev *)ei_status.priv; - if (idev) - pnp_device_detach(idev); -#endif - release_region(dev->base_addr - ULTRA_NIC_OFFSET, ULTRA_IO_EXTENT); - iounmap(ei_status.mem); -} - -static void __exit ultra_cleanup_module(void) -{ - int this_dev; - - for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) { - struct net_device *dev = dev_ultra[this_dev]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -module_exit(ultra_cleanup_module); -#endif /* MODULE */ diff --git a/drivers/net/ethernet/8390/wd.c b/drivers/net/ethernet/8390/wd.c deleted file mode 100644 index ffd639477dfc..000000000000 --- a/drivers/net/ethernet/8390/wd.c +++ /dev/null @@ -1,575 +0,0 @@ -// SPDX-License-Identifier: GPL-1.0+ -/* wd.c: A WD80x3 ethernet driver for linux. */ -/* - Written 1993-94 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This is a driver for WD8003 and WD8013 "compatible" ethercards. - - Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013. - - Changelog: - - Paul Gortmaker : multiple card support for module users, support - for non-standard memory sizes. - - -*/ - -static const char version[] = - "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <net/Space.h> - -#include <asm/io.h> - -#include "8390.h" - -#define DRV_NAME "wd" - -/* A zero-terminated list of I/O addresses to be probed. */ -static unsigned int wd_portlist[] __initdata = -{0x300, 0x280, 0x380, 0x240, 0}; - -static int wd_probe1(struct net_device *dev, int ioaddr); - -static int wd_open(struct net_device *dev); -static void wd_reset_8390(struct net_device *dev); -static void wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, - int ring_page); -static void wd_block_input(struct net_device *dev, int count, - struct sk_buff *skb, int ring_offset); -static void wd_block_output(struct net_device *dev, int count, - const unsigned char *buf, int start_page); -static int wd_close(struct net_device *dev); - -static u32 wd_msg_enable; - -#define WD_START_PG 0x00 /* First page of TX buffer */ -#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ -#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ - -#define WD_CMDREG 0 /* Offset to ASIC command register. */ -#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */ -#define WD_MEMENB 0x40 /* Enable the shared memory. */ -#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */ -#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */ -#define NIC16 0x40 /* Enable 16 bit access from the 8390. */ -#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */ -#define WD_IO_EXTENT 32 - - -/* Probe for the WD8003 and WD8013. These cards have the station - address PROM at I/O ports <base>+8 to <base>+13, with a checksum - following. A Soundblaster can have the same checksum as an WDethercard, - so we have an extra exclusionary check for it. - - The wd_probe1() routine initializes the card and fills the - station address field. */ - -static int __init do_wd_probe(struct net_device *dev) -{ - int i; - struct resource *r; - int base_addr = dev->base_addr; - int irq = dev->irq; - int mem_start = dev->mem_start; - int mem_end = dev->mem_end; - - if (base_addr > 0x1ff) { /* Check a user specified location. */ - r = request_region(base_addr, WD_IO_EXTENT, "wd-probe"); - if ( r == NULL) - return -EBUSY; - i = wd_probe1(dev, base_addr); - if (i != 0) - release_region(base_addr, WD_IO_EXTENT); - else - r->name = dev->name; - return i; - } - else if (base_addr != 0) /* Don't probe at all. */ - return -ENXIO; - - for (i = 0; wd_portlist[i]; i++) { - int ioaddr = wd_portlist[i]; - r = request_region(ioaddr, WD_IO_EXTENT, "wd-probe"); - if (r == NULL) - continue; - if (wd_probe1(dev, ioaddr) == 0) { - r->name = dev->name; - return 0; - } - release_region(ioaddr, WD_IO_EXTENT); - dev->irq = irq; - dev->mem_start = mem_start; - dev->mem_end = mem_end; - } - - return -ENODEV; -} - -#ifndef MODULE -struct net_device * __init wd_probe(int unit) -{ - struct net_device *dev = alloc_ei_netdev(); - int err; - - if (!dev) - return ERR_PTR(-ENOMEM); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - err = do_wd_probe(dev); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); -} -#endif - -static const struct net_device_ops wd_netdev_ops = { - .ndo_open = wd_open, - .ndo_stop = wd_close, - .ndo_start_xmit = ei_start_xmit, - .ndo_tx_timeout = ei_tx_timeout, - .ndo_get_stats = ei_get_stats, - .ndo_set_rx_mode = ei_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = ei_poll, -#endif -}; - -static int __init wd_probe1(struct net_device *dev, int ioaddr) -{ - int i; - int err; - int checksum = 0; - int ancient = 0; /* An old card without config registers. */ - int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */ - u8 addr[ETH_ALEN]; - const char *model_name; - static unsigned version_printed; - struct ei_device *ei_local = netdev_priv(dev); - - for (i = 0; i < 8; i++) - checksum += inb(ioaddr + 8 + i); - if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */ - || inb(ioaddr + 9) == 0xff - || (checksum & 0xff) != 0xFF) - return -ENODEV; - - /* Check for semi-valid mem_start/end values if supplied. */ - if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) { - netdev_warn(dev, - "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n"); - dev->mem_start = 0; - dev->mem_end = 0; - } - - if ((wd_msg_enable & NETIF_MSG_DRV) && (version_printed++ == 0)) - netdev_info(dev, version); - - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + 8 + i); - eth_hw_addr_set(dev, addr); - - netdev_info(dev, "WD80x3 at %#3x, %pM", ioaddr, dev->dev_addr); - - /* The following PureData probe code was contributed by - Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software - configuration differently from others so we have to check for them. - This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card. - */ - if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') { - unsigned char reg5 = inb(ioaddr+5); - - switch (inb(ioaddr+2)) { - case 0x03: word16 = 0; model_name = "PDI8023-8"; break; - case 0x05: word16 = 0; model_name = "PDUC8023"; break; - case 0x0a: word16 = 1; model_name = "PDI8023-16"; break; - /* Either 0x01 (dumb) or they've released a new version. */ - default: word16 = 0; model_name = "PDI8023"; break; - } - dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12; - dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1; - } else { /* End of PureData probe */ - /* This method of checking for a 16-bit board is borrowed from the - we.c driver. A simpler method is just to look in ASIC reg. 0x03. - I'm comparing the two method in alpha test to make certain they - return the same result. */ - /* Check for the old 8 bit board - it has register 0/8 aliasing. - Do NOT check i>=6 here -- it hangs the old 8003 boards! */ - for (i = 0; i < 6; i++) - if (inb(ioaddr+i) != inb(ioaddr+8+i)) - break; - if (i >= 6) { - ancient = 1; - model_name = "WD8003-old"; - word16 = 0; - } else { - int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */ - outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */ - if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */ - && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */ - int asic_reg5 = inb(ioaddr+WD_CMDREG5); - /* Magic to set ASIC to word-wide mode. */ - outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5); - outb(tmp, ioaddr+1); - model_name = "WD8013"; - word16 = 1; /* We have a 16bit board here! */ - } else { - model_name = "WD8003"; - word16 = 0; - } - outb(tmp, ioaddr+1); /* Restore original reg1 value. */ - } -#ifndef final_version - if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01)) - pr_cont("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).", - word16 ? 16 : 8, - (inb(ioaddr+1) & 0x01) ? 16 : 8); -#endif - } - -#if defined(WD_SHMEM) && WD_SHMEM > 0x80000 - /* Allow a compile-time override. */ - dev->mem_start = WD_SHMEM; -#else - if (dev->mem_start == 0) { - /* Sanity and old 8003 check */ - int reg0 = inb(ioaddr); - if (reg0 == 0xff || reg0 == 0) { - /* Future plan: this could check a few likely locations first. */ - dev->mem_start = 0xd0000; - pr_cont(" assigning address %#lx", dev->mem_start); - } else { - int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f; - /* Some boards don't have the register 5 -- it returns 0xff. */ - if (high_addr_bits == 0x1f || word16 == 0) - high_addr_bits = 0x01; - dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19); - } - } -#endif - - /* The 8390 isn't at the base address -- the ASIC regs are there! */ - dev->base_addr = ioaddr+WD_NIC_OFFSET; - - if (dev->irq < 2) { - static const int irqmap[] = {9, 3, 5, 7, 10, 11, 15, 4}; - int reg1 = inb(ioaddr+1); - int reg4 = inb(ioaddr+4); - if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */ - short nic_addr = ioaddr+WD_NIC_OFFSET; - unsigned long irq_mask; - - /* We have an old-style ethercard that doesn't report its IRQ - line. Do autoirq to find the IRQ line. Note that this IS NOT - a reliable way to trigger an interrupt. */ - outb_p(E8390_NODMA + E8390_STOP, nic_addr); - outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */ - - irq_mask = probe_irq_on(); - outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */ - outb_p(0x00, nic_addr + EN0_RCNTLO); - outb_p(0x00, nic_addr + EN0_RCNTHI); - outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */ - mdelay(20); - dev->irq = probe_irq_off(irq_mask); - - outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */ - - if (wd_msg_enable & NETIF_MSG_PROBE) - pr_cont(" autoirq is %d", dev->irq); - if (dev->irq < 2) - dev->irq = word16 ? 10 : 5; - } else - dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)]; - } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */ - dev->irq = 9; - - /* Snarf the interrupt now. There's no point in waiting since we cannot - share and the board will usually be enabled. */ - i = request_irq(dev->irq, ei_interrupt, 0, DRV_NAME, dev); - if (i) { - pr_cont(" unable to get IRQ %d.\n", dev->irq); - return i; - } - - /* OK, were are certain this is going to work. Setup the device. */ - ei_status.name = model_name; - ei_status.word16 = word16; - ei_status.tx_start_page = WD_START_PG; - ei_status.rx_start_page = WD_START_PG + TX_PAGES; - - /* Don't map in the shared memory until the board is actually opened. */ - - /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */ - if (dev->mem_end != 0) { - ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; - ei_status.priv = dev->mem_end - dev->mem_start; - } else { - ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG; - dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256; - ei_status.priv = (ei_status.stop_page - WD_START_PG)*256; - } - - ei_status.mem = ioremap(dev->mem_start, ei_status.priv); - if (!ei_status.mem) { - free_irq(dev->irq, dev); - return -ENOMEM; - } - - pr_cont(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", - model_name, dev->irq, dev->mem_start, dev->mem_end-1); - - ei_status.reset_8390 = wd_reset_8390; - ei_status.block_input = wd_block_input; - ei_status.block_output = wd_block_output; - ei_status.get_8390_hdr = wd_get_8390_hdr; - - dev->netdev_ops = &wd_netdev_ops; - NS8390_init(dev, 0); - ei_local->msg_enable = wd_msg_enable; - -#if 1 - /* Enable interrupt generation on softconfig cards -- M.U */ - /* .. but possibly potentially unsafe - Donald */ - if (inb(ioaddr+14) & 0x20) - outb(inb(ioaddr+4)|0x80, ioaddr+4); -#endif - - err = register_netdev(dev); - if (err) { - free_irq(dev->irq, dev); - iounmap(ei_status.mem); - } - return err; -} - -static int -wd_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - - /* Map in the shared memory. Always set register 0 last to remain - compatible with very old boards. */ - ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB; - ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16; - - if (ei_status.word16) - outb(ei_status.reg5, ioaddr+WD_CMDREG5); - outb(ei_status.reg0, ioaddr); /* WD_CMDREG */ - - return ei_open(dev); -} - -static void -wd_reset_8390(struct net_device *dev) -{ - int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - struct ei_device *ei_local = netdev_priv(dev); - - outb(WD_RESET, wd_cmd_port); - netif_dbg(ei_local, hw, dev, "resetting the WD80x3 t=%lu...\n", - jiffies); - ei_status.txing = 0; - - /* Set up the ASIC registers, just in case something changed them. */ - outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); - if (ei_status.word16) - outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5); - - netif_dbg(ei_local, hw, dev, "reset done\n"); -} - -/* Grab the 8390 specific header. Similar to the block_input routine, but - we don't need to be concerned with ring wrap as the header will be at - the start of a page, so we optimize accordingly. */ - -static void -wd_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) -{ - - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - void __iomem *hdr_start = ei_status.mem + ((ring_page - WD_START_PG)<<8); - - /* We'll always get a 4 byte header read followed by a packet read, so - we enable 16 bit mode before the header, and disable after the body. */ - if (ei_status.word16) - outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); - -#ifdef __BIG_ENDIAN - /* Officially this is what we are doing, but the readl() is faster */ - /* unfortunately it isn't endian aware of the struct */ - memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr)); - hdr->count = le16_to_cpu(hdr->count); -#else - ((unsigned int*)hdr)[0] = readl(hdr_start); -#endif -} - -/* Block input and output are easy on shared memory ethercards, and trivial - on the Western digital card where there is no choice of how to do it. - The only complications are that the ring buffer wraps, and need to map - switch between 8- and 16-bit modes. */ - -static void -wd_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) -{ - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - unsigned long offset = ring_offset - (WD_START_PG<<8); - void __iomem *xfer_start = ei_status.mem + offset; - - if (offset + count > ei_status.priv) { - /* We must wrap the input move. */ - int semi_count = ei_status.priv - offset; - memcpy_fromio(skb->data, xfer_start, semi_count); - count -= semi_count; - memcpy_fromio(skb->data + semi_count, ei_status.mem + TX_PAGES * 256, count); - } else { - /* Packet is in one chunk -- we can copy + cksum. */ - memcpy_fromio(skb->data, xfer_start, count); - } - - /* Turn off 16 bit access so that reboot works. ISA brain-damage */ - if (ei_status.word16) - outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); -} - -static void -wd_block_output(struct net_device *dev, int count, const unsigned char *buf, - int start_page) -{ - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - void __iomem *shmem = ei_status.mem + ((start_page - WD_START_PG)<<8); - - - if (ei_status.word16) { - /* Turn on and off 16 bit access so that reboot works. */ - outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5); - memcpy_toio(shmem, buf, count); - outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5); - } else - memcpy_toio(shmem, buf, count); -} - - -static int -wd_close(struct net_device *dev) -{ - int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */ - struct ei_device *ei_local = netdev_priv(dev); - - netif_dbg(ei_local, ifdown, dev, "Shutting down ethercard.\n"); - ei_close(dev); - - /* Change from 16-bit to 8-bit shared memory so reboot works. */ - if (ei_status.word16) - outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 ); - - /* And disable the shared memory. */ - outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg); - - return 0; -} - - -#ifdef MODULE -#define MAX_WD_CARDS 4 /* Max number of wd cards per module */ -static struct net_device *dev_wd[MAX_WD_CARDS]; -static int io[MAX_WD_CARDS]; -static int irq[MAX_WD_CARDS]; -static int mem[MAX_WD_CARDS]; -static int mem_end[MAX_WD_CARDS]; /* for non std. mem size */ - -module_param_hw_array(io, int, ioport, NULL, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param_hw_array(mem, int, iomem, NULL, 0); -module_param_hw_array(mem_end, int, iomem, NULL, 0); -module_param_named(msg_enable, wd_msg_enable, uint, 0444); -MODULE_PARM_DESC(io, "I/O base address(es)"); -MODULE_PARM_DESC(irq, "IRQ number(s) (ignored for PureData boards)"); -MODULE_PARM_DESC(mem, "memory base address(es)(ignored for PureData boards)"); -MODULE_PARM_DESC(mem_end, "memory end address(es)"); -MODULE_PARM_DESC(msg_enable, "Debug message level (see linux/netdevice.h for bitmap)"); -MODULE_DESCRIPTION("ISA Western Digital wd8003/wd8013 ; SMC Elite, Elite16 ethernet driver"); -MODULE_LICENSE("GPL"); - -/* This is set up so that only a single autoprobe takes place per call. -ISA device autoprobes on a running machine are not recommended. */ - -static int __init wd_init_module(void) -{ - struct net_device *dev; - int this_dev, found = 0; - - for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { - if (io[this_dev] == 0) { - if (this_dev != 0) break; /* only autoprobe 1st one */ - printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n"); - } - dev = alloc_ei_netdev(); - if (!dev) - break; - dev->irq = irq[this_dev]; - dev->base_addr = io[this_dev]; - dev->mem_start = mem[this_dev]; - dev->mem_end = mem_end[this_dev]; - if (do_wd_probe(dev) == 0) { - dev_wd[found++] = dev; - continue; - } - free_netdev(dev); - printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]); - break; - } - if (found) - return 0; - return -ENXIO; -} -module_init(wd_init_module); - -static void cleanup_card(struct net_device *dev) -{ - free_irq(dev->irq, dev); - release_region(dev->base_addr - WD_NIC_OFFSET, WD_IO_EXTENT); - iounmap(ei_status.mem); -} - -static void __exit wd_cleanup_module(void) -{ - int this_dev; - - for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) { - struct net_device *dev = dev_wd[this_dev]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -module_exit(wd_cleanup_module); -#endif /* MODULE */ diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index bdc29d143160..b8f70e2a1763 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -61,7 +61,6 @@ source "drivers/net/ethernet/engleder/Kconfig" source "drivers/net/ethernet/ezchip/Kconfig" source "drivers/net/ethernet/faraday/Kconfig" source "drivers/net/ethernet/freescale/Kconfig" -source "drivers/net/ethernet/fujitsu/Kconfig" source "drivers/net/ethernet/fungible/Kconfig" source "drivers/net/ethernet/google/Kconfig" source "drivers/net/ethernet/hisilicon/Kconfig" @@ -157,7 +156,6 @@ config OA_TC6 To know the implementation details, refer documentation in <file:Documentation/networking/oa-tc6-framework.rst>. -source "drivers/net/ethernet/packetengines/Kconfig" source "drivers/net/ethernet/pasemi/Kconfig" source "drivers/net/ethernet/pensando/Kconfig" source "drivers/net/ethernet/qlogic/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 6bffb60ba644..57344fec6ce0 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_NET_VENDOR_ENGLEDER) += engleder/ obj-$(CONFIG_NET_VENDOR_EZCHIP) += ezchip/ obj-$(CONFIG_NET_VENDOR_FARADAY) += faraday/ obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/ -obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/ obj-$(CONFIG_NET_VENDOR_FUNGIBLE) += fungible/ obj-$(CONFIG_NET_VENDOR_GOOGLE) += google/ obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/ @@ -73,7 +72,6 @@ obj-$(CONFIG_NET_VENDOR_NVIDIA) += nvidia/ obj-$(CONFIG_LPC_ENET) += nxp/ obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/ obj-$(CONFIG_ETHOC) += ethoc.o -obj-$(CONFIG_NET_VENDOR_PACKET_ENGINES) += packetengines/ obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index e1ab15f1ee7d..d0c0c0ec8a80 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -745,14 +745,18 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q, dma_addr_t dma_addr; q->buf_size = PAGE_SIZE / 2; - q->ndesc = ndesc; q->qdma = qdma; - q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), + q->entry = devm_kzalloc(eth->dev, ndesc * sizeof(*q->entry), GFP_KERNEL); if (!q->entry) return -ENOMEM; + q->desc = dmam_alloc_coherent(eth->dev, ndesc * sizeof(*q->desc), + &dma_addr, GFP_KERNEL); + if (!q->desc) + return -ENOMEM; + q->page_pool = page_pool_create(&pp_params); if (IS_ERR(q->page_pool)) { int err = PTR_ERR(q->page_pool); @@ -761,11 +765,7 @@ static int airoha_qdma_init_rx_queue(struct airoha_queue *q, return err; } - q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), - &dma_addr, GFP_KERNEL); - if (!q->desc) - return -ENOMEM; - + q->ndesc = ndesc; netif_napi_add(eth->napi_dev, &q->napi, airoha_qdma_rx_napi_poll); airoha_qdma_wr(qdma, REG_RX_RING_BASE(qid), dma_addr); @@ -843,6 +843,32 @@ static int airoha_qdma_init_rx(struct airoha_qdma *qdma) return 0; } +static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) +{ + struct airoha_qdma *qdma = q->qdma; + struct airoha_eth *eth = qdma->eth; + int i, qid = q - &qdma->q_tx[0]; + + for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { + struct airoha_gdm_port *port = eth->ports[i]; + int j; + + if (!port) + continue; + + if (port->qdma != qdma) + continue; + + for (j = 0; j < port->dev->num_tx_queues; j++) { + if (airoha_qdma_get_txq(qdma, j) != qid) + continue; + + netif_wake_subqueue(port->dev, j); + } + } + q->txq_stopped = false; +} + static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) { struct airoha_tx_irq_queue *irq_q; @@ -914,17 +940,25 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) q->queued--; if (skb) { - u16 queue = skb_get_queue_mapping(skb); struct netdev_queue *txq; - txq = netdev_get_tx_queue(skb->dev, queue); + txq = skb_get_tx_queue(skb->dev, skb); netdev_tx_completed_queue(txq, 1, skb->len); - if (netif_tx_queue_stopped(txq) && - q->ndesc - q->queued >= q->free_thr) - netif_tx_wake_queue(txq); - dev_kfree_skb_any(skb); } + + if (q->txq_stopped && q->ndesc - q->queued >= q->free_thr) { + /* Since multiple net_device TX queues can share the + * same hw QDMA TX queue, there is no guarantee we have + * inflight packets queued in hw belonging to a + * net_device TX queue stopped in the xmit path. + * In order to avoid any potential net_device TX queue + * stall, we need to wake all the net_device TX queues + * feeding the same hw QDMA TX queue. + */ + airoha_qdma_wake_netdev_txqs(q); + } + unlock: spin_unlock_bh(&q->lock); } @@ -954,27 +988,27 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q, dma_addr_t dma_addr; spin_lock_init(&q->lock); - q->ndesc = size; q->qdma = qdma; q->free_thr = 1 + MAX_SKB_FRAGS; INIT_LIST_HEAD(&q->tx_list); - q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry), + q->entry = devm_kzalloc(eth->dev, size * sizeof(*q->entry), GFP_KERNEL); if (!q->entry) return -ENOMEM; - q->desc = dmam_alloc_coherent(eth->dev, q->ndesc * sizeof(*q->desc), + q->desc = dmam_alloc_coherent(eth->dev, size * sizeof(*q->desc), &dma_addr, GFP_KERNEL); if (!q->desc) return -ENOMEM; - for (i = 0; i < q->ndesc; i++) { + for (i = 0; i < size; i++) { u32 val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1); list_add_tail(&q->entry[i].list, &q->tx_list); WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val)); } + q->ndesc = size; /* xmit ring drop default setting */ airoha_qdma_set(qdma, REG_TX_RING_BLOCKING(qid), @@ -996,8 +1030,6 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, struct airoha_eth *eth = qdma->eth; dma_addr_t dma_addr; - netif_napi_add_tx(eth->napi_dev, &irq_q->napi, - airoha_qdma_tx_napi_poll); irq_q->q = dmam_alloc_coherent(eth->dev, size * sizeof(u32), &dma_addr, GFP_KERNEL); if (!irq_q->q) @@ -1007,6 +1039,9 @@ static int airoha_qdma_tx_irq_init(struct airoha_tx_irq_queue *irq_q, irq_q->size = size; irq_q->qdma = qdma; + netif_napi_add_tx(eth->napi_dev, &irq_q->napi, + airoha_qdma_tx_napi_poll); + airoha_qdma_wr(qdma, REG_TX_IRQ_BASE(id), dma_addr); airoha_qdma_rmw(qdma, REG_TX_IRQ_CFG(id), TX_IRQ_DEPTH_MASK, FIELD_PREP(TX_IRQ_DEPTH_MASK, size)); @@ -1039,12 +1074,15 @@ static int airoha_qdma_init_tx(struct airoha_qdma *qdma) static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) { - struct airoha_eth *eth = q->qdma->eth; - int i; + struct airoha_qdma *qdma = q->qdma; + struct airoha_eth *eth = qdma->eth; + int i, qid = q - &qdma->q_tx[0]; + u16 index = 0; spin_lock_bh(&q->lock); for (i = 0; i < q->ndesc; i++) { struct airoha_queue_entry *e = &q->entry[i]; + struct airoha_qdma_desc *desc = &q->desc[i]; if (!e->dma_addr) continue; @@ -1055,8 +1093,33 @@ static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q) e->dma_addr = 0; e->skb = NULL; list_add_tail(&e->list, &q->tx_list); + + /* Reset DMA descriptor */ + WRITE_ONCE(desc->ctrl, 0); + WRITE_ONCE(desc->addr, 0); + WRITE_ONCE(desc->data, 0); + WRITE_ONCE(desc->msg0, 0); + WRITE_ONCE(desc->msg1, 0); + WRITE_ONCE(desc->msg2, 0); + q->queued--; } + + if (!list_empty(&q->tx_list)) { + struct airoha_queue_entry *e; + + e = list_first_entry(&q->tx_list, struct airoha_queue_entry, + list); + index = e - q->entry; + } + /* Set TX_DMA_IDX to TX_CPU_IDX to notify the hw the QDMA TX ring is + * empty. + */ + airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, + FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); + airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK, + FIELD_PREP(TX_RING_DMA_IDX_MASK, index)); + spin_unlock_bh(&q->lock); } @@ -1398,8 +1461,12 @@ static void airoha_qdma_cleanup(struct airoha_qdma *qdma) } } - for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) + for (i = 0; i < ARRAY_SIZE(qdma->q_tx_irq); i++) { + if (!qdma->q_tx_irq[i].size) + continue; + netif_napi_del(&qdma->q_tx_irq[i].napi); + } for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) { if (!qdma->q_tx[i].ndesc) @@ -1680,14 +1747,11 @@ static int airoha_dev_stop(struct net_device *dev) { struct airoha_gdm_port *port = netdev_priv(dev); struct airoha_qdma *qdma = port->qdma; - int i, err; + int i; netif_tx_disable(dev); - err = airoha_set_vip_for_gdm_port(port, false); - if (err) - return err; - - for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) + airoha_set_vip_for_gdm_port(port, false); + for (i = 0; i < dev->num_tx_queues; i++) netdev_tx_reset_subqueue(dev, i); airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), @@ -1727,7 +1791,7 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) { struct airoha_eth *eth = port->qdma->eth; u32 val, pse_port, chan; - int src_port; + int i, src_port; /* Forward the traffic to the proper GDM port */ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 @@ -1769,6 +1833,9 @@ static int airoha_set_gdm2_loopback(struct airoha_gdm_port *port) SP_CPORT_MASK(val), __field_prep(SP_CPORT_MASK(val), FE_PSE_PORT_CDM2)); + for (i = 0; i < eth->soc->num_ppe; i++) + airoha_ppe_set_cpu_port(port, i, AIROHA_GDM2_IDX); + if (port->id == AIROHA_GDM4_IDX && airoha_is_7581(eth)) { u32 mask = FC_ID_OF_SRC_PORT_MASK(port->nbq); @@ -1807,7 +1874,8 @@ static int airoha_dev_init(struct net_device *dev) } for (i = 0; i < eth->soc->num_ppe; i++) - airoha_ppe_set_cpu_port(port, i); + airoha_ppe_set_cpu_port(port, i, + airoha_get_fe_port(port)); return 0; } @@ -1936,12 +2004,12 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, struct netdev_queue *txq; struct airoha_queue *q; LIST_HEAD(tx_list); + int i = 0, qid; void *data; - int i, qid; u16 index; u8 fport; - qid = skb_get_queue_mapping(skb) % ARRAY_SIZE(qdma->q_tx); + qid = airoha_qdma_get_txq(qdma, skb_get_queue_mapping(skb)); tag = airoha_get_dsa_tag(skb, dev); msg0 = FIELD_PREP(QDMA_ETH_TXMSG_CHAN_MASK, @@ -1978,12 +2046,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, spin_lock_bh(&q->lock); - txq = netdev_get_tx_queue(dev, qid); + txq = skb_get_tx_queue(dev, skb); nr_frags = 1 + skb_shinfo(skb)->nr_frags; if (q->queued + nr_frags >= q->ndesc) { /* not enough space in the queue */ netif_tx_stop_queue(txq); + q->txq_stopped = true; spin_unlock_bh(&q->lock); return NETDEV_TX_BUSY; } @@ -1995,7 +2064,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, list); index = e - q->entry; - for (i = 0; i < nr_frags; i++) { + while (true) { struct airoha_qdma_desc *desc = &q->desc[index]; skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; dma_addr_t addr; @@ -2007,7 +2076,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, goto error_unmap; list_move_tail(&e->list, &tx_list); - e->skb = i ? NULL : skb; + e->skb = i == nr_frags - 1 ? skb : NULL; e->dma_addr = addr; e->dma_len = len; @@ -2026,6 +2095,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, WRITE_ONCE(desc->msg1, cpu_to_le32(msg1)); WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff)); + if (++i == nr_frags) + break; + data = skb_frag_address(frag); len = skb_frag_size(frag); } @@ -2033,28 +2105,27 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, skb_tx_timestamp(skb); netdev_tx_sent_queue(txq, skb->len); + if (q->ndesc - q->queued < q->free_thr) { + netif_tx_stop_queue(txq); + q->txq_stopped = true; + } if (netif_xmit_stopped(txq) || !netdev_xmit_more()) airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK, FIELD_PREP(TX_RING_CPU_IDX_MASK, index)); - if (q->ndesc - q->queued < q->free_thr) - netif_tx_stop_queue(txq); - spin_unlock_bh(&q->lock); return NETDEV_TX_OK; error_unmap: - while (!list_empty(&tx_list)) { - e = list_first_entry(&tx_list, struct airoha_queue_entry, - list); + list_for_each_entry(e, &tx_list, list) { dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len, DMA_TO_DEVICE); e->dma_addr = 0; - list_move_tail(&e->list, &q->tx_list); } + list_splice(&tx_list, &q->tx_list); spin_unlock_bh(&q->lock); error: diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index 95e557638617..4fad3acc3ccf 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -193,6 +193,7 @@ struct airoha_queue { int ndesc; int free_thr; int buf_size; + bool txq_stopped; struct napi_struct napi; struct page_pool *page_pool; @@ -630,6 +631,11 @@ u32 airoha_rmw(void __iomem *base, u32 offset, u32 mask, u32 val); #define airoha_qdma_clear(qdma, offset, val) \ airoha_rmw((qdma)->regs, (offset), (val), 0) +static inline u16 airoha_qdma_get_txq(struct airoha_qdma *qdma, u16 qid) +{ + return qid % ARRAY_SIZE(qdma->q_tx); +} + static inline bool airoha_is_lan_gdm_port(struct airoha_gdm_port *port) { /* GDM1 port on EN7581 SoC is connected to the lan dsa switch. @@ -653,7 +659,8 @@ int airoha_get_fe_port(struct airoha_gdm_port *port); bool airoha_is_valid_gdm_port(struct airoha_eth *eth, struct airoha_gdm_port *port); -void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id); +void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, + u8 fport); bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, u16 hash, bool rx_wlan); diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 03115c1c1063..5c9dff6bccd1 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -85,10 +85,9 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe) return FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, timestamp); } -void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id) +void airoha_ppe_set_cpu_port(struct airoha_gdm_port *port, u8 ppe_id, u8 fport) { struct airoha_qdma *qdma = port->qdma; - u8 fport = airoha_get_fe_port(port); struct airoha_eth *eth = qdma->eth; u8 qdma_id = qdma - ð->qdma[0]; u32 fe_cpu_port; @@ -182,7 +181,8 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) if (!port) continue; - airoha_ppe_set_cpu_port(port, i); + airoha_ppe_set_cpu_port(port, i, + airoha_get_fe_port(port)); } } } @@ -1356,6 +1356,29 @@ static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth) return npu; } +static int airoha_ppe_wait_for_npu_init(struct airoha_eth *eth) +{ + int err; + u32 val; + + /* PPE_FLOW_CFG default register value is 0. Since we reset FE + * during the device probe we can just check the configured value + * is not 0 here. + */ + err = read_poll_timeout(airoha_fe_rr, val, val, USEC_PER_MSEC, + 100 * USEC_PER_MSEC, false, eth, + REG_PPE_PPE_FLOW_CFG(0)); + if (err) + return err; + + if (airoha_ppe_is_enabled(eth, 1)) + err = read_poll_timeout(airoha_fe_rr, val, val, USEC_PER_MSEC, + 100 * USEC_PER_MSEC, false, eth, + REG_PPE_PPE_FLOW_CFG(1)); + + return err; +} + static int airoha_ppe_offload_setup(struct airoha_eth *eth) { struct airoha_npu *npu = airoha_ppe_npu_get(eth); @@ -1369,6 +1392,11 @@ static int airoha_ppe_offload_setup(struct airoha_eth *eth) if (err) goto error_npu_put; + /* Wait for NPU PPE configuration to complete */ + err = airoha_ppe_wait_for_npu_init(eth); + if (err) + goto error_npu_put; + ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe); if (ppe_num_stats_entries > 0) { err = npu->ops.ppe_init_stats(npu, ppe->foe_stats_dma, diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index e67b592e5697..8c86789d867a 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -1782,20 +1782,23 @@ void ena_com_phc_destroy(struct ena_com_dev *ena_dev) int ena_com_phc_get_timestamp(struct ena_com_dev *ena_dev, u64 *timestamp) { - volatile struct ena_admin_phc_resp *resp = ena_dev->phc.virt_addr; const ktime_t zero_system_time = ktime_set(0, 0); struct ena_com_phc_info *phc = &ena_dev->phc; + volatile struct ena_admin_phc_resp *resp; ktime_t expire_time; ktime_t block_time; unsigned long flags = 0; int ret = 0; + spin_lock_irqsave(&phc->lock, flags); + if (!phc->active) { + spin_unlock_irqrestore(&phc->lock, flags); netdev_err(ena_dev->net_device, "PHC feature is not active in the device\n"); return -EOPNOTSUPP; } - spin_lock_irqsave(&phc->lock, flags); + resp = ena_dev->phc.virt_addr; /* Check if PHC is in blocked state */ if (unlikely(ktime_compare(phc->system_time, zero_system_time))) { diff --git a/drivers/net/ethernet/amazon/ena/ena_phc.c b/drivers/net/ethernet/amazon/ena/ena_phc.c index 7867e893fd15..c2a3ff1ef645 100644 --- a/drivers/net/ethernet/amazon/ena/ena_phc.c +++ b/drivers/net/ethernet/amazon/ena/ena_phc.c @@ -46,9 +46,12 @@ static int ena_phc_gettimex64(struct ptp_clock_info *clock_info, spin_unlock_irqrestore(&phc_info->lock, flags); + if (rc) + return rc; + *ts = ns_to_timespec64(timestamp_nsec); - return rc; + return 0; } static int ena_phc_settime64(struct ptp_clock_info *clock_info, diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 45e8d698781c..e35991141a1a 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -43,17 +43,6 @@ config AMD8111_ETH To compile this driver as a module, choose M here. The module will be called amd8111e. -config LANCE - tristate "AMD LANCE and PCnet (AT1500 and NE2100) support" - depends on ISA && ISA_DMA_API && !ARM && !PPC32 - select NETDEV_LEGACY_INIT - help - If you have a network (Ethernet) card of this type, say Y here. - Some LinkSys cards are of this type. - - To compile this driver as a module, choose M here: the module - will be called lance. This is recommended. - config PCNET32 tristate "AMD PCnet32 PCI support" depends on PCI && HAS_IOPORT @@ -120,16 +109,6 @@ config MVME147_NET driver for this chip in your kernel. To compile this driver as a module, choose M here. -config PCMCIA_NMCLAN - tristate "New Media PCMCIA support" - depends on PCMCIA && HAS_IOPORT - help - Say Y here if you intend to attach a New Media Ethernet or LiveWire - PCMCIA (PC-card) Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called nmclan_cs. If unsure, say N. - config SUN3LANCE tristate "Sun3/Sun3x on-board LANCE support" depends on (SUN3 || SUN3X) diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile index 2dcfb84731e1..e485fae235a7 100644 --- a/drivers/net/ethernet/amd/Makefile +++ b/drivers/net/ethernet/amd/Makefile @@ -9,10 +9,8 @@ obj-$(CONFIG_ARIADNE) += ariadne.o obj-$(CONFIG_ATARILANCE) += atarilance.o obj-$(CONFIG_DECLANCE) += declance.o obj-$(CONFIG_HPLANCE) += hplance.o 7990.o -obj-$(CONFIG_LANCE) += lance.o obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o -obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o obj-$(CONFIG_PCNET32) += pcnet32.o obj-$(CONFIG_SUN3LANCE) += sun3lance.o obj-$(CONFIG_SUNLANCE) += sunlance.o diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c deleted file mode 100644 index 98afd8cb0efb..000000000000 --- a/drivers/net/ethernet/amd/lance.c +++ /dev/null @@ -1,1317 +0,0 @@ -/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */ -/* - Written/copyright 1993-1998 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - This driver is for the Allied Telesis AT1500 and HP J2405A, and should work - with most other LANCE-based bus-master (NE2100/NE2500) ethercards. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Andrey V. Savochkin: - - alignment problem with 1.3.* kernel and some minor changes. - Thomas Bogendoerfer (tsbogend@bigbug.franken.de): - - added support for Linux/Alpha, but removed most of it, because - it worked only for the PCI chip. - - added hook for the 32bit lance driver - - added PCnetPCI II (79C970A) to chip table - Paul Gortmaker (gpg109@rsphy1.anu.edu.au): - - hopefully fix above so Linux/Alpha can use ISA cards too. - 8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb - v1.12 10/27/97 Module support -djb - v1.14 2/3/98 Module support modified, made PCI support optional -djb - v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed - before unregister_netdev() which caused NULL pointer - reference later in the chain (in rtnetlink_fill_ifinfo()) - -- Mika Kuoppala <miku@iki.fi> - - Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from - the 2.1 version of the old driver - Alan Cox - - Get rid of check_region, check kmalloc return in lance_probe1 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001 - - Reworked detection, added support for Racal InterLan EtherBlaster cards - Vesselin Kostadinov <vesok at yahoo dot com > - 22/4/2004 -*/ - -static const char version[] = "lance.c:v1.16 2006/11/09 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n"; - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/mm.h> -#include <linux/bitops.h> -#include <net/Space.h> - -#include <asm/io.h> -#include <asm/dma.h> - -static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0}; -static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options); -static int __init do_lance_probe(struct net_device *dev); - - -static struct card { - char id_offset14; - char id_offset15; -} cards[] = { - { //"normal" - .id_offset14 = 0x57, - .id_offset15 = 0x57, - }, - { //NI6510EB - .id_offset14 = 0x52, - .id_offset15 = 0x44, - }, - { //Racal InterLan EtherBlaster - .id_offset14 = 0x52, - .id_offset15 = 0x49, - }, -}; -#define NUM_CARDS 3 - -#ifdef LANCE_DEBUG -static int lance_debug = LANCE_DEBUG; -#else -static int lance_debug = 1; -#endif - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the AMD 79C960, the "PCnet-ISA -single-chip ethernet controller for ISA". This chip is used in a wide -variety of boards from vendors such as Allied Telesis, HP, Kingston, -and Boca. This driver is also intended to work with older AMD 7990 -designs, such as the NE1500 and NE2100, and newer 79C961. For convenience, -I use the name LANCE to refer to all of the AMD chips, even though it properly -refers only to the original 7990. - -II. Board-specific settings - -The driver is designed to work the boards that use the faster -bus-master mode, rather than in shared memory mode. (Only older designs -have on-board buffer memory needed to support the slower shared memory mode.) - -Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA -channel. This driver probes the likely base addresses: -{0x300, 0x320, 0x340, 0x360}. -After the board is found it generates a DMA-timeout interrupt and uses -autoIRQ to find the IRQ line. The DMA channel can be set with the low bits -of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is -probed for by enabling each free DMA channel in turn and checking if -initialization succeeds. - -The HP-J2405A board is an exception: with this board it is easy to read the -EEPROM-set values for the base, IRQ, and DMA. (Of course you must already -_know_ the base address -- that field is for writing the EEPROM.) - -III. Driver operation - -IIIa. Ring buffers -The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes -the base and length of the data buffer, along with status bits. The length -of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of -the buffer length (rather than being directly the buffer length) for -implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to -ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries -needlessly uses extra space and reduces the chance that an upper layer will -be able to reorder queued Tx packets based on priority. Decreasing the number -of entries makes it more difficult to achieve back-to-back packet transmission -and increases the chance that Rx ring will overflow. (Consider the worst case -of receiving back-to-back minimum-sized packets.) - -The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver -statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to -avoid the administrative overhead. For the Rx side this avoids dynamically -allocating full-sized buffers "just in case", at the expense of a -memory-to-memory data copy for each packet received. For most systems this -is a good tradeoff: the Rx buffer will always be in low memory, the copy -is inexpensive, and it primes the cache for later packet processing. For Tx -the buffers are only used when needed as low-memory bounce buffers. - -IIIB. 16M memory limitations. -For the ISA bus master mode all structures used directly by the LANCE, -the initialization block, Rx and Tx rings, and data buffers, must be -accessible from the ISA bus, i.e. in the lower 16M of real memory. -This is a problem for current Linux kernels on >16M machines. The network -devices are initialized after memory initialization, and the kernel doles out -memory from the top of memory downward. The current solution is to have a -special network initialization routine that's called before memory -initialization; this will eventually be generalized for all network devices. -As mentioned before, low-memory "bounce-buffers" are used when needed. - -IIIC. Synchronization -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'lp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so -we can't avoid the interrupt overhead by having the Tx routine reap the Tx -stats.) After reaping the stats, it marks the queue entry as empty by setting -the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the -tx_full and tbusy flags. - -*/ - -/* Set the number of Tx and Rx buffers, using Log_2(# buffers). - Reasonable default values are 16 Tx buffers, and 16 Rx buffers. - That translates to 4 and 4 (16 == 2^^4). - This is a compile-time option for efficiency. - */ -#ifndef LANCE_LOG_TX_BUFFERS -#define LANCE_LOG_TX_BUFFERS 4 -#define LANCE_LOG_RX_BUFFERS 4 -#endif - -#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) -#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) -#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29) - -#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) -#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) -#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29) - -#define PKT_BUF_SZ 1544 - -/* Offsets from base I/O address. */ -#define LANCE_DATA 0x10 -#define LANCE_ADDR 0x12 -#define LANCE_RESET 0x14 -#define LANCE_BUS_IF 0x16 -#define LANCE_TOTAL_SIZE 0x18 - -#define TX_TIMEOUT (HZ/5) - -/* The LANCE Rx and Tx ring descriptors. */ -struct lance_rx_head { - s32 base; - s16 buf_length; /* This length is 2s complement (negative)! */ - s16 msg_length; /* This length is "normal". */ -}; - -struct lance_tx_head { - s32 base; - s16 length; /* Length is 2s complement (negative)! */ - s16 misc; -}; - -/* The LANCE initialization block, described in databook. */ -struct lance_init_block { - u16 mode; /* Pre-set mode (reg. 15) */ - u8 phys_addr[6]; /* Physical ethernet address */ - u32 filter[2]; /* Multicast filter (unused). */ - /* Receive and transmit ring base, along with extra bits. */ - u32 rx_ring; /* Tx and Rx ring base pointers */ - u32 tx_ring; -}; - -struct lance_private { - /* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */ - struct lance_rx_head rx_ring[RX_RING_SIZE]; - struct lance_tx_head tx_ring[TX_RING_SIZE]; - struct lance_init_block init_block; - const char *name; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - /* The addresses of receive-in-place skbuffs. */ - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - unsigned long rx_buffs; /* Address of Rx and Tx buffers. */ - /* Tx low-memory "bounce buffer" address. */ - char (*tx_bounce_buffs)[PKT_BUF_SZ]; - int cur_rx, cur_tx; /* The next free ring entry */ - int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ - int dma; - unsigned char chip_version; /* See lance_chip_type. */ - spinlock_t devlock; -}; - -#define LANCE_MUST_PAD 0x00000001 -#define LANCE_ENABLE_AUTOSELECT 0x00000002 -#define LANCE_MUST_REINIT_RING 0x00000004 -#define LANCE_MUST_UNRESET 0x00000008 -#define LANCE_HAS_MISSED_FRAME 0x00000010 - -/* A mapping from the chip ID number to the part number and features. - These are from the datasheets -- in real life the '970 version - reportedly has the same ID as the '965. */ -static struct lance_chip_type { - int id_number; - const char *name; - int flags; -} chip_table[] = { - {0x0000, "LANCE 7990", /* Ancient lance chip. */ - LANCE_MUST_PAD + LANCE_MUST_UNRESET}, - {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call - it the PCnet32. */ - {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */ - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, - {0x0, "PCnet (unknown)", - LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING + - LANCE_HAS_MISSED_FRAME}, -}; - -enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6}; - - -/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers. - Assume yes until we know the memory size. */ -static unsigned char lance_need_isa_bounce_buffers = 1; - -static int lance_open(struct net_device *dev); -static void lance_init_ring(struct net_device *dev, gfp_t mode); -static netdev_tx_t lance_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static int lance_rx(struct net_device *dev); -static irqreturn_t lance_interrupt(int irq, void *dev_id); -static int lance_close(struct net_device *dev); -static struct net_device_stats *lance_get_stats(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue); - - - -#ifdef MODULE -#define MAX_CARDS 8 /* Max number of interfaces (cards) per module */ - -static struct net_device *dev_lance[MAX_CARDS]; -static int io[MAX_CARDS]; -static int dma[MAX_CARDS]; -static int irq[MAX_CARDS]; - -module_param_hw_array(io, int, ioport, NULL, 0); -module_param_hw_array(dma, int, dma, NULL, 0); -module_param_hw_array(irq, int, irq, NULL, 0); -module_param(lance_debug, int, 0); -MODULE_PARM_DESC(io, "LANCE/PCnet I/O base address(es),required"); -MODULE_PARM_DESC(dma, "LANCE/PCnet ISA DMA channel (ignored for some devices)"); -MODULE_PARM_DESC(irq, "LANCE/PCnet IRQ number (ignored for some devices)"); -MODULE_PARM_DESC(lance_debug, "LANCE/PCnet debug level (0-7)"); - -static int __init lance_init_module(void) -{ - struct net_device *dev; - int this_dev, found = 0; - - for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { - if (io[this_dev] == 0) { - if (this_dev != 0) /* only complain once */ - break; - printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n"); - return -EPERM; - } - dev = alloc_etherdev(0); - if (!dev) - break; - dev->irq = irq[this_dev]; - dev->base_addr = io[this_dev]; - dev->dma = dma[this_dev]; - if (do_lance_probe(dev) == 0) { - dev_lance[found++] = dev; - continue; - } - free_netdev(dev); - break; - } - if (found != 0) - return 0; - return -ENXIO; -} -module_init(lance_init_module); - -static void cleanup_card(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - if (dev->dma != 4) - free_dma(dev->dma); - release_region(dev->base_addr, LANCE_TOTAL_SIZE); - kfree(lp->tx_bounce_buffs); - kfree((void*)lp->rx_buffs); - kfree(lp); -} - -static void __exit lance_cleanup_module(void) -{ - int this_dev; - - for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) { - struct net_device *dev = dev_lance[this_dev]; - if (dev) { - unregister_netdev(dev); - cleanup_card(dev); - free_netdev(dev); - } - } -} -module_exit(lance_cleanup_module); -#endif /* MODULE */ -MODULE_DESCRIPTION("AMD LANCE/PCnet Ethernet driver"); -MODULE_LICENSE("GPL"); - - -/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other - board probes now that kmalloc() can allocate ISA DMA-able regions. - This also allows the LANCE driver to be used as a module. - */ -static int __init do_lance_probe(struct net_device *dev) -{ - unsigned int *port; - int result; - - if (high_memory <= phys_to_virt(16*1024*1024)) - lance_need_isa_bounce_buffers = 0; - - for (port = lance_portlist; *port; port++) { - int ioaddr = *port; - struct resource *r = request_region(ioaddr, LANCE_TOTAL_SIZE, - "lance-probe"); - - if (r) { - /* Detect the card with minimal I/O reads */ - char offset14 = inb(ioaddr + 14); - int card; - for (card = 0; card < NUM_CARDS; ++card) - if (cards[card].id_offset14 == offset14) - break; - if (card < NUM_CARDS) {/*yes, the first byte matches*/ - char offset15 = inb(ioaddr + 15); - for (card = 0; card < NUM_CARDS; ++card) - if ((cards[card].id_offset14 == offset14) && - (cards[card].id_offset15 == offset15)) - break; - } - if (card < NUM_CARDS) { /*Signature OK*/ - result = lance_probe1(dev, ioaddr, 0, 0); - if (!result) { - struct lance_private *lp = dev->ml_priv; - int ver = lp->chip_version; - - r->name = chip_table[ver].name; - return 0; - } - } - release_region(ioaddr, LANCE_TOTAL_SIZE); - } - } - return -ENODEV; -} - -#ifndef MODULE -struct net_device * __init lance_probe(int unit) -{ - struct net_device *dev = alloc_etherdev(0); - int err; - - if (!dev) - return ERR_PTR(-ENODEV); - - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - - err = do_lance_probe(dev); - if (err) - goto out; - return dev; -out: - free_netdev(dev); - return ERR_PTR(err); -} -#endif - -static const struct net_device_ops lance_netdev_ops = { - .ndo_open = lance_open, - .ndo_start_xmit = lance_start_xmit, - .ndo_stop = lance_close, - .ndo_get_stats = lance_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_tx_timeout = lance_tx_timeout, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options) -{ - struct lance_private *lp; - unsigned long dma_channels; /* Mark spuriously-busy DMA channels */ - int i, reset_val, lance_version; - const char *chipname; - /* Flags for specific chips or boards. */ - unsigned char hpJ2405A = 0; /* HP ISA adaptor */ - int hp_builtin = 0; /* HP on-board ethernet. */ - static int did_version; /* Already printed version info. */ - unsigned long flags; - int err = -ENOMEM; - void __iomem *bios; - u8 addr[ETH_ALEN]; - - /* First we look for special cases. - Check for HP's on-board ethernet by looking for 'HP' in the BIOS. - There are two HP versions, check the BIOS for the configuration port. - This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com. - */ - bios = ioremap(0xf00f0, 0x14); - if (!bios) - return -ENOMEM; - if (readw(bios + 0x12) == 0x5048) { - static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360}; - int hp_port = (readl(bios + 1) & 1) ? 0x499 : 0x99; - /* We can have boards other than the built-in! Verify this is on-board. */ - if ((inb(hp_port) & 0xc0) == 0x80 && - ioaddr_table[inb(hp_port) & 3] == ioaddr) - hp_builtin = hp_port; - } - iounmap(bios); - /* We also recognize the HP Vectra on-board here, but check below. */ - hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 && - inb(ioaddr+2) == 0x09); - - /* Reset the LANCE. */ - reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */ - - /* The Un-Reset needed is only needed for the real NE2100, and will - confuse the HP board. */ - if (!hpJ2405A) - outw(reset_val, ioaddr+LANCE_RESET); - - outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */ - if (inw(ioaddr+LANCE_DATA) != 0x0004) - return -ENODEV; - - /* Get the version of the chip. */ - outw(88, ioaddr+LANCE_ADDR); - if (inw(ioaddr+LANCE_ADDR) != 88) { - lance_version = 0; - } else { /* Good, it's a newer chip. */ - int chip_version = inw(ioaddr+LANCE_DATA); - outw(89, ioaddr+LANCE_ADDR); - chip_version |= inw(ioaddr+LANCE_DATA) << 16; - if (lance_debug > 2) - printk(" LANCE chip version is %#x.\n", chip_version); - if ((chip_version & 0xfff) != 0x003) - return -ENODEV; - chip_version = (chip_version >> 12) & 0xffff; - for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) { - if (chip_table[lance_version].id_number == chip_version) - break; - } - } - - /* We can't allocate private data from alloc_etherdev() because it must - a ISA DMA-able region. */ - chipname = chip_table[lance_version].name; - printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr); - - /* There is a 16 byte station address PROM at the base address. - The first six bytes are the station address. */ - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + i); - eth_hw_addr_set(dev, addr); - printk("%pM", dev->dev_addr); - - dev->base_addr = ioaddr; - /* Make certain the data structures used by the LANCE are aligned and DMAble. */ - - lp = kzalloc_obj(*lp, GFP_DMA | GFP_KERNEL); - if (!lp) - return -ENOMEM; - if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp); - dev->ml_priv = lp; - lp->name = chipname; - lp->rx_buffs = (unsigned long)kmalloc_array(RX_RING_SIZE, PKT_BUF_SZ, - GFP_DMA | GFP_KERNEL); - if (!lp->rx_buffs) - goto out_lp; - if (lance_need_isa_bounce_buffers) { - lp->tx_bounce_buffs = kmalloc_array(TX_RING_SIZE, PKT_BUF_SZ, - GFP_DMA | GFP_KERNEL); - if (!lp->tx_bounce_buffs) - goto out_rx; - } else - lp->tx_bounce_buffs = NULL; - - lp->chip_version = lance_version; - spin_lock_init(&lp->devlock); - - lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */ - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; - lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; - - outw(0x0001, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); - outw(0x0002, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); - outw(0x0000, ioaddr+LANCE_ADDR); - inw(ioaddr+LANCE_ADDR); - - if (irq) { /* Set iff PCI card. */ - dev->dma = 4; /* Native bus-master, no DMA channel needed. */ - dev->irq = irq; - } else if (hp_builtin) { - static const char dma_tbl[4] = {3, 5, 6, 0}; - static const char irq_tbl[4] = {3, 4, 5, 9}; - unsigned char port_val = inb(hp_builtin); - dev->dma = dma_tbl[(port_val >> 4) & 3]; - dev->irq = irq_tbl[(port_val >> 2) & 3]; - printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma); - } else if (hpJ2405A) { - static const char dma_tbl[4] = {3, 5, 6, 7}; - static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15}; - short reset_val = inw(ioaddr+LANCE_RESET); - dev->dma = dma_tbl[(reset_val >> 2) & 3]; - dev->irq = irq_tbl[(reset_val >> 4) & 7]; - printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma); - } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */ - short bus_info; - outw(8, ioaddr+LANCE_ADDR); - bus_info = inw(ioaddr+LANCE_BUS_IF); - dev->dma = bus_info & 0x07; - dev->irq = (bus_info >> 4) & 0x0F; - } else { - /* The DMA channel may be passed in PARAM1. */ - if (dev->mem_start & 0x07) - dev->dma = dev->mem_start & 0x07; - } - - if (dev->dma == 0) { - /* Read the DMA channel status register, so that we can avoid - stuck DMA channels in the DMA detection below. */ - dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) | - (inb(DMA2_STAT_REG) & 0xf0); - } - err = -ENODEV; - if (dev->irq >= 2) - printk(" assigned IRQ %d", dev->irq); - else if (lance_version != 0) { /* 7990 boards need DMA detection first. */ - unsigned long irq_mask; - - /* To auto-IRQ we enable the initialization-done and DMA error - interrupts. For ISA boards we get a DMA error, but VLB and PCI - boards will work. */ - irq_mask = probe_irq_on(); - - /* Trigger an initialization just for the interrupt. */ - outw(0x0041, ioaddr+LANCE_DATA); - - mdelay(20); - dev->irq = probe_irq_off(irq_mask); - if (dev->irq) - printk(", probed IRQ %d", dev->irq); - else { - printk(", failed to detect IRQ line.\n"); - goto out_tx; - } - - /* Check for the initialization done bit, 0x0100, which means - that we don't need a DMA channel. */ - if (inw(ioaddr+LANCE_DATA) & 0x0100) - dev->dma = 4; - } - - if (dev->dma == 4) { - printk(", no DMA needed.\n"); - } else if (dev->dma) { - if (request_dma(dev->dma, chipname)) { - printk("DMA %d allocation failed.\n", dev->dma); - goto out_tx; - } else - printk(", assigned DMA %d.\n", dev->dma); - } else { /* OK, we have to auto-DMA. */ - for (i = 0; i < 4; i++) { - static const char dmas[] = { 5, 6, 7, 3 }; - int dma = dmas[i]; - int boguscnt; - - /* Don't enable a permanently busy DMA channel, or the machine - will hang. */ - if (test_bit(dma, &dma_channels)) - continue; - outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */ - if (request_dma(dma, chipname)) - continue; - - flags=claim_dma_lock(); - set_dma_mode(dma, DMA_MODE_CASCADE); - enable_dma(dma); - release_dma_lock(flags); - - /* Trigger an initialization. */ - outw(0x0001, ioaddr+LANCE_DATA); - for (boguscnt = 100; boguscnt > 0; --boguscnt) - if (inw(ioaddr+LANCE_DATA) & 0x0900) - break; - if (inw(ioaddr+LANCE_DATA) & 0x0100) { - dev->dma = dma; - printk(", DMA %d.\n", dev->dma); - break; - } else { - flags=claim_dma_lock(); - disable_dma(dma); - release_dma_lock(flags); - free_dma(dma); - } - } - if (i == 4) { /* Failure: bail. */ - printk("DMA detection failed.\n"); - goto out_tx; - } - } - - if (lance_version == 0 && dev->irq == 0) { - /* We may auto-IRQ now that we have a DMA channel. */ - /* Trigger an initialization just for the interrupt. */ - unsigned long irq_mask; - - irq_mask = probe_irq_on(); - outw(0x0041, ioaddr+LANCE_DATA); - - mdelay(40); - dev->irq = probe_irq_off(irq_mask); - if (dev->irq == 0) { - printk(" Failed to detect the 7990 IRQ line.\n"); - goto out_dma; - } - printk(" Auto-IRQ detected IRQ%d.\n", dev->irq); - } - - if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { - /* Turn on auto-select of media (10baseT or BNC) so that the user - can watch the LEDs even if the board isn't opened. */ - outw(0x0002, ioaddr+LANCE_ADDR); - /* Don't touch 10base2 power bit. */ - outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); - } - - if (lance_debug > 0 && did_version++ == 0) - printk(version); - - /* The LANCE-specific entries in the device structure. */ - dev->netdev_ops = &lance_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - err = register_netdev(dev); - if (err) - goto out_dma; - return 0; -out_dma: - if (dev->dma != 4) - free_dma(dev->dma); -out_tx: - kfree(lp->tx_bounce_buffs); -out_rx: - kfree((void*)lp->rx_buffs); -out_lp: - kfree(lp); - return err; -} - - -static int -lance_open(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int ioaddr = dev->base_addr; - int i; - - if (dev->irq == 0 || - request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) { - return -EAGAIN; - } - - /* We used to allocate DMA here, but that was silly. - DMA lines can't be shared! We now permanently allocate them. */ - - /* Reset the LANCE */ - inw(ioaddr+LANCE_RESET); - - /* The DMA controller is used as a no-operation slave, "cascade mode". */ - if (dev->dma != 4) { - unsigned long flags=claim_dma_lock(); - enable_dma(dev->dma); - set_dma_mode(dev->dma, DMA_MODE_CASCADE); - release_dma_lock(flags); - } - - /* Un-Reset the LANCE, needed only for the NE2100. */ - if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET) - outw(0, ioaddr+LANCE_RESET); - - if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) { - /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */ - outw(0x0002, ioaddr+LANCE_ADDR); - /* Only touch autoselect bit. */ - outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF); - } - - if (lance_debug > 1) - printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n", - dev->name, dev->irq, dev->dma, - (u32) isa_virt_to_bus(lp->tx_ring), - (u32) isa_virt_to_bus(lp->rx_ring), - (u32) isa_virt_to_bus(&lp->init_block)); - - lance_init_ring(dev, GFP_KERNEL); - /* Re-initialize the LANCE, and start it when done. */ - outw(0x0001, ioaddr+LANCE_ADDR); - outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA); - outw(0x0002, ioaddr+LANCE_ADDR); - outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA); - - outw(0x0004, ioaddr+LANCE_ADDR); - outw(0x0915, ioaddr+LANCE_DATA); - - outw(0x0000, ioaddr+LANCE_ADDR); - outw(0x0001, ioaddr+LANCE_DATA); - - netif_start_queue (dev); - - i = 0; - while (i++ < 100) - if (inw(ioaddr+LANCE_DATA) & 0x0100) - break; - /* - * We used to clear the InitDone bit, 0x0100, here but Mark Stockton - * reports that doing so triggers a bug in the '974. - */ - outw(0x0042, ioaddr+LANCE_DATA); - - if (lance_debug > 2) - printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n", - dev->name, i, (u32) isa_virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA)); - - return 0; /* Always succeed */ -} - -/* The LANCE has been halted for one reason or another (busmaster memory - arbitration error, Tx FIFO underflow, driver stopped it to reconfigure, - etc.). Modern LANCE variants always reload their ring-buffer - configuration when restarted, so we must reinitialize our ring - context before restarting. As part of this reinitialization, - find all packets still on the Tx ring and pretend that they had been - sent (in effect, drop the packets on the floor) - the higher-level - protocols will time out and retransmit. It'd be better to shuffle - these skbs to a temp list and then actually re-Tx them after - restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com -*/ - -static void -lance_purge_ring(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int i; - - /* Free all the skbuffs in the Rx and Tx queues. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = lp->rx_skbuff[i]; - lp->rx_skbuff[i] = NULL; - lp->rx_ring[i].base = 0; /* Not owned by LANCE chip. */ - if (skb) - dev_kfree_skb_any(skb); - } - for (i = 0; i < TX_RING_SIZE; i++) { - if (lp->tx_skbuff[i]) { - dev_kfree_skb_any(lp->tx_skbuff[i]); - lp->tx_skbuff[i] = NULL; - } - } -} - - -/* Initialize the LANCE Rx and Tx rings. */ -static void -lance_init_ring(struct net_device *dev, gfp_t gfp) -{ - struct lance_private *lp = dev->ml_priv; - int i; - - lp->cur_rx = lp->cur_tx = 0; - lp->dirty_rx = lp->dirty_tx = 0; - - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb; - void *rx_buff; - - skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp); - lp->rx_skbuff[i] = skb; - if (skb) - rx_buff = skb->data; - else - rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp); - if (!rx_buff) - lp->rx_ring[i].base = 0; - else - lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000; - lp->rx_ring[i].buf_length = -PKT_BUF_SZ; - } - /* The Tx buffer address is filled in as needed, but we do need to clear - the upper ownership bit. */ - for (i = 0; i < TX_RING_SIZE; i++) { - lp->tx_skbuff[i] = NULL; - lp->tx_ring[i].base = 0; - } - - lp->init_block.mode = 0x0000; - for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; - lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; -} - -static void -lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit) -{ - struct lance_private *lp = dev->ml_priv; - - if (must_reinit || - (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) { - lance_purge_ring(dev); - lance_init_ring(dev, GFP_ATOMIC); - } - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(csr0_bits, dev->base_addr + LANCE_DATA); -} - - -static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue) -{ - struct lance_private *lp = (struct lance_private *) dev->ml_priv; - int ioaddr = dev->base_addr; - - outw (0, ioaddr + LANCE_ADDR); - printk ("%s: transmit timed out, status %4.4x, resetting.\n", - dev->name, inw (ioaddr + LANCE_DATA)); - outw (0x0004, ioaddr + LANCE_DATA); - dev->stats.tx_errors++; -#ifndef final_version - if (lance_debug > 3) { - int i; - printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", - lp->dirty_tx, lp->cur_tx, netif_queue_stopped(dev) ? " (full)" : "", - lp->cur_rx); - for (i = 0; i < RX_RING_SIZE; i++) - printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", - lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, - lp->rx_ring[i].msg_length); - for (i = 0; i < TX_RING_SIZE; i++) - printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ", - lp->tx_ring[i].base, -lp->tx_ring[i].length, - lp->tx_ring[i].misc); - printk ("\n"); - } -#endif - lance_restart (dev, 0x0043, 1); - - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue (dev); -} - - -static netdev_tx_t lance_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int ioaddr = dev->base_addr; - int entry; - unsigned long flags; - - spin_lock_irqsave(&lp->devlock, flags); - - if (lance_debug > 3) { - outw(0x0000, ioaddr+LANCE_ADDR); - printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name, - inw(ioaddr+LANCE_DATA)); - outw(0x0000, ioaddr+LANCE_DATA); - } - - /* Fill in a Tx ring entry */ - - /* Mask to ring buffer boundary. */ - entry = lp->cur_tx & TX_RING_MOD_MASK; - - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ - - /* The old LANCE chips doesn't automatically pad buffers to min. size. */ - if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) { - if (skb->len < ETH_ZLEN) { - if (skb_padto(skb, ETH_ZLEN)) - goto out; - lp->tx_ring[entry].length = -ETH_ZLEN; - } - else - lp->tx_ring[entry].length = -skb->len; - } else - lp->tx_ring[entry].length = -skb->len; - - lp->tx_ring[entry].misc = 0x0000; - - dev->stats.tx_bytes += skb->len; - - /* If any part of this buffer is >16M we must copy it to a low-memory - buffer. */ - if ((u32)isa_virt_to_bus(skb->data) + skb->len > 0x01000000) { - if (lance_debug > 5) - printk("%s: bouncing a high-memory packet (%#x).\n", - dev->name, (u32)isa_virt_to_bus(skb->data)); - skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len); - lp->tx_ring[entry].base = - ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000; - dev_consume_skb_irq(skb); - } else { - lp->tx_skbuff[entry] = skb; - lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000; - } - lp->cur_tx++; - - /* Trigger an immediate send poll. */ - outw(0x0000, ioaddr+LANCE_ADDR); - outw(0x0048, ioaddr+LANCE_DATA); - - if ((lp->cur_tx - lp->dirty_tx) >= TX_RING_SIZE) - netif_stop_queue(dev); - -out: - spin_unlock_irqrestore(&lp->devlock, flags); - return NETDEV_TX_OK; -} - -/* The LANCE interrupt handler. */ -static irqreturn_t lance_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct lance_private *lp; - int csr0, ioaddr, boguscnt=10; - int must_restart; - - ioaddr = dev->base_addr; - lp = dev->ml_priv; - - spin_lock (&lp->devlock); - - outw(0x00, dev->base_addr + LANCE_ADDR); - while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 && - --boguscnt >= 0) { - /* Acknowledge all of the current interrupt sources ASAP. */ - outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA); - - must_restart = 0; - - if (lance_debug > 5) - printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n", - dev->name, csr0, inw(dev->base_addr + LANCE_DATA)); - - if (csr0 & 0x0400) /* Rx interrupt */ - lance_rx(dev); - - if (csr0 & 0x0200) { /* Tx-done interrupt */ - int dirty_tx = lp->dirty_tx; - - while (dirty_tx < lp->cur_tx) { - int entry = dirty_tx & TX_RING_MOD_MASK; - int status = lp->tx_ring[entry].base; - - if (status < 0) - break; /* It still hasn't been Txed */ - - lp->tx_ring[entry].base = 0; - - if (status & 0x40000000) { - /* There was an major error, log it. */ - int err_status = lp->tx_ring[entry].misc; - dev->stats.tx_errors++; - if (err_status & 0x0400) - dev->stats.tx_aborted_errors++; - if (err_status & 0x0800) - dev->stats.tx_carrier_errors++; - if (err_status & 0x1000) - dev->stats.tx_window_errors++; - if (err_status & 0x4000) { - /* Ackk! On FIFO errors the Tx unit is turned off! */ - dev->stats.tx_fifo_errors++; - /* Remove this verbosity later! */ - printk("%s: Tx FIFO error! Status %4.4x.\n", - dev->name, csr0); - /* Restart the chip. */ - must_restart = 1; - } - } else { - if (status & 0x18000000) - dev->stats.collisions++; - dev->stats.tx_packets++; - } - - /* We must free the original skb if it's not a data-only copy - in the bounce buffer. */ - if (lp->tx_skbuff[entry]) { - dev_consume_skb_irq(lp->tx_skbuff[entry]); - lp->tx_skbuff[entry] = NULL; - } - dirty_tx++; - } - -#ifndef final_version - if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) { - printk("out-of-sync dirty pointer, %d vs. %d, full=%s.\n", - dirty_tx, lp->cur_tx, - netif_queue_stopped(dev) ? "yes" : "no"); - dirty_tx += TX_RING_SIZE; - } -#endif - - /* if the ring is no longer full, accept more packets */ - if (netif_queue_stopped(dev) && - dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) - netif_wake_queue (dev); - - lp->dirty_tx = dirty_tx; - } - - /* Log misc errors. */ - if (csr0 & 0x4000) - dev->stats.tx_errors++; /* Tx babble. */ - if (csr0 & 0x1000) - dev->stats.rx_errors++; /* Missed a Rx frame. */ - if (csr0 & 0x0800) { - printk("%s: Bus master arbitration failure, status %4.4x.\n", - dev->name, csr0); - /* Restart the chip. */ - must_restart = 1; - } - - if (must_restart) { - /* stop the chip to clear the error condition, then restart */ - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(0x0004, dev->base_addr + LANCE_DATA); - lance_restart(dev, 0x0002, 0); - } - } - - /* Clear any other interrupt, and set interrupt enable. */ - outw(0x0000, dev->base_addr + LANCE_ADDR); - outw(0x7940, dev->base_addr + LANCE_DATA); - - if (lance_debug > 4) - printk("%s: exiting interrupt, csr%d=%#4.4x.\n", - dev->name, inw(ioaddr + LANCE_ADDR), - inw(dev->base_addr + LANCE_DATA)); - - spin_unlock (&lp->devlock); - return IRQ_HANDLED; -} - -static int -lance_rx(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - int entry = lp->cur_rx & RX_RING_MOD_MASK; - int i; - - /* If we own the next entry, it's a new packet. Send it up. */ - while (lp->rx_ring[entry].base >= 0) { - int status = lp->rx_ring[entry].base >> 24; - - if (status != 0x03) { /* There was an error. */ - /* There is a tricky error noted by John Murphy, - <murf@perftech.com> to Russ Nelson: Even with full-sized - buffers it's possible for a jabber packet to use two - buffers, with only the last correctly noting the error. */ - if (status & 0x01) /* Only count a general error at the */ - dev->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x20) - dev->stats.rx_frame_errors++; - if (status & 0x10) - dev->stats.rx_over_errors++; - if (status & 0x08) - dev->stats.rx_crc_errors++; - if (status & 0x04) - dev->stats.rx_fifo_errors++; - lp->rx_ring[entry].base &= 0x03ffffff; - } - else - { - /* Malloc up new buffer, compatible with net3. */ - short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4; - struct sk_buff *skb; - - if(pkt_len<60) - { - printk("%s: Runt packet!\n",dev->name); - dev->stats.rx_errors++; - } - else - { - skb = dev_alloc_skb(pkt_len+2); - if (!skb) - { - printk("%s: Memory squeeze, deferring packet.\n", dev->name); - for (i=0; i < RX_RING_SIZE; i++) - if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0) - break; - - if (i > RX_RING_SIZE -2) - { - dev->stats.rx_dropped++; - lp->rx_ring[entry].base |= 0x80000000; - lp->cur_rx++; - } - break; - } - skb_reserve(skb,2); /* 16 byte align */ - skb_put(skb,pkt_len); /* Make room */ - skb_copy_to_linear_data(skb, - (unsigned char *)isa_bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)), - pkt_len); - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - } - /* The docs say that the buffer length isn't touched, but Andrew Boyd - of QNX reports that some revs of the 79C965 clear it. */ - lp->rx_ring[entry].buf_length = -PKT_BUF_SZ; - lp->rx_ring[entry].base |= 0x80000000; - entry = (++lp->cur_rx) & RX_RING_MOD_MASK; - } - - /* We should check that at least two ring entries are free. If not, - we should free one and mark stats->rx_dropped++. */ - - return 0; -} - -static int -lance_close(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - struct lance_private *lp = dev->ml_priv; - - netif_stop_queue (dev); - - if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { - outw(112, ioaddr+LANCE_ADDR); - dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); - } - outw(0, ioaddr+LANCE_ADDR); - - if (lance_debug > 1) - printk("%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, inw(ioaddr+LANCE_DATA)); - - /* We stop the LANCE here -- it occasionally polls - memory if we don't. */ - outw(0x0004, ioaddr+LANCE_DATA); - - if (dev->dma != 4) - { - unsigned long flags=claim_dma_lock(); - disable_dma(dev->dma); - release_dma_lock(flags); - } - free_irq(dev->irq, dev); - - lance_purge_ring(dev); - - return 0; -} - -static struct net_device_stats *lance_get_stats(struct net_device *dev) -{ - struct lance_private *lp = dev->ml_priv; - - if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) { - short ioaddr = dev->base_addr; - short saved_addr; - unsigned long flags; - - spin_lock_irqsave(&lp->devlock, flags); - saved_addr = inw(ioaddr+LANCE_ADDR); - outw(112, ioaddr+LANCE_ADDR); - dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA); - outw(saved_addr, ioaddr+LANCE_ADDR); - spin_unlock_irqrestore(&lp->devlock, flags); - } - - return &dev->stats; -} - -/* Set or clear the multicast filter for this adaptor. - */ - -static void set_multicast_list(struct net_device *dev) -{ - short ioaddr = dev->base_addr; - - outw(0, ioaddr+LANCE_ADDR); - outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */ - - if (dev->flags&IFF_PROMISC) { - outw(15, ioaddr+LANCE_ADDR); - outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */ - } else { - short multicast_table[4]; - int i; - int num_addrs=netdev_mc_count(dev); - if(dev->flags&IFF_ALLMULTI) - num_addrs=1; - /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */ - memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table)); - for (i = 0; i < 4; i++) { - outw(8 + i, ioaddr+LANCE_ADDR); - outw(multicast_table[i], ioaddr+LANCE_DATA); - } - outw(15, ioaddr+LANCE_ADDR); - outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */ - } - - lance_restart(dev, 0x0142, 0); /* Resume normal operation */ - -} - diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c deleted file mode 100644 index 37054a670407..000000000000 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ /dev/null @@ -1,1508 +0,0 @@ -/* ---------------------------------------------------------------------------- -Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN. - nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao - - The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media - Access Controller for Ethernet (MACE). It is essentially the Am2150 - PCMCIA Ethernet card contained in the Am2150 Demo Kit. - -Written by Roger C. Pao <rpao@paonet.org> - Copyright 1995 Roger C. Pao - Linux 2.5 cleanups Copyright Red Hat 2003 - - This software may be used and distributed according to the terms of - the GNU General Public License. - -Ported to Linux 1.3.* network driver environment by - Matti Aarnio <mea@utu.fi> - -References - - Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993 - Am79C940 (MACE) Data Sheet, 1994 - Am79C90 (C-LANCE) Data Sheet, 1994 - Linux PCMCIA Programmer's Guide v1.17 - /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8 - - Eric Mears, New Media Corporation - Tom Pollard, New Media Corporation - Dean Siasoyco, New Media Corporation - Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com> - Donald Becker <becker@scyld.com> - David Hinds <dahinds@users.sourceforge.net> - - The Linux client driver is based on the 3c589_cs.c client driver by - David Hinds. - - The Linux network driver outline is based on the 3c589_cs.c driver, - the 8390.c driver, and the example skeleton.c kernel code, which are - by Donald Becker. - - The Am2150 network driver hardware interface code is based on the - OS/9000 driver for the New Media Ethernet LAN by Eric Mears. - - Special thanks for testing and help in debugging this driver goes - to Ken Lesniak. - -------------------------------------------------------------------------------- -Driver Notes and Issues -------------------------------------------------------------------------------- - -1. Developed on a Dell 320SLi - PCMCIA Card Services 2.6.2 - Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386 - -2. rc.pcmcia may require loading pcmcia_core with io_speed=300: - 'insmod pcmcia_core.o io_speed=300'. - This will avoid problems with fast systems which causes rx_framecnt - to return random values. - -3. If hot extraction does not work for you, use 'ifconfig eth0 down' - before extraction. - -4. There is a bad slow-down problem in this driver. - -5. Future: Multicast processing. In the meantime, do _not_ compile your - kernel with multicast ip enabled. - -------------------------------------------------------------------------------- -History -------------------------------------------------------------------------------- -Log: nmclan_cs.c,v - * 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk> - * Fixed hang on card eject as we probe it - * Cleaned up to use new style locking. - * - * Revision 0.16 1995/07/01 06:42:17 rpao - * Bug fix: nmclan_reset() called CardServices incorrectly. - * - * Revision 0.15 1995/05/24 08:09:47 rpao - * Re-implement MULTI_TX dev->tbusy handling. - * - * Revision 0.14 1995/05/23 03:19:30 rpao - * Added, in nmclan_config(), "tuple.Attributes = 0;". - * Modified MACE ID check to ignore chip revision level. - * Avoid tx_free_frames race condition between _start_xmit and _interrupt. - * - * Revision 0.13 1995/05/18 05:56:34 rpao - * Statistics changes. - * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list. - * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT. Fixes driver lockup. - * - * Revision 0.12 1995/05/14 00:12:23 rpao - * Statistics overhaul. - * - -95/05/13 rpao V0.10a - Bug fix: MACE statistics counters used wrong I/O ports. - Bug fix: mace_interrupt() needed to allow statistics to be - processed without RX or TX interrupts pending. -95/05/11 rpao V0.10 - Multiple transmit request processing. - Modified statistics to use MACE counters where possible. -95/05/10 rpao V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO. - *Released -95/05/10 rpao V0.08 - Bug fix: Make all non-exported functions private by using - static keyword. - Bug fix: Test IntrCnt _before_ reading MACE_IR. -95/05/10 rpao V0.07 Statistics. -95/05/09 rpao V0.06 Fix rx_framecnt problem by addition of PCIC wait states. - ----------------------------------------------------------------------------- */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "nmclan_cs" - -/* ---------------------------------------------------------------------------- -Conditional Compilation Options ----------------------------------------------------------------------------- */ - -#define MULTI_TX 0 -#define RESET_ON_TIMEOUT 1 -#define TX_INTERRUPTABLE 1 -#define RESET_XILINX 0 - -/* ---------------------------------------------------------------------------- -Include Files ----------------------------------------------------------------------------- */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/delay.h> -#include <linux/ethtool.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ioport.h> -#include <linux/bitops.h> - -#include <pcmcia/cisreg.h> -#include <pcmcia/cistpl.h> -#include <pcmcia/ds.h> - -#include <linux/uaccess.h> -#include <asm/io.h> - -/* ---------------------------------------------------------------------------- -Defines ----------------------------------------------------------------------------- */ - -#define MACE_LADRF_LEN 8 - /* 8 bytes in Logical Address Filter */ - -/* Loop Control Defines */ -#define MACE_MAX_IR_ITERATIONS 10 -#define MACE_MAX_RX_ITERATIONS 12 - /* - TBD: Dean brought this up, and I assumed the hardware would - handle it: - - If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be - non-zero when the isr exits. We may not get another interrupt - to process the remaining packets for some time. - */ - -/* -The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA) -which manages the interface between the MACE and the PCMCIA bus. It -also includes buffer management for the 32K x 8 SRAM to control up to -four transmit and 12 receive frames at a time. -*/ -#define AM2150_MAX_TX_FRAMES 4 -#define AM2150_MAX_RX_FRAMES 12 - -/* Am2150 Ethernet Card I/O Mapping */ -#define AM2150_RCV 0x00 -#define AM2150_XMT 0x04 -#define AM2150_XMT_SKIP 0x09 -#define AM2150_RCV_NEXT 0x0A -#define AM2150_RCV_FRAME_COUNT 0x0B -#define AM2150_MACE_BANK 0x0C -#define AM2150_MACE_BASE 0x10 - -/* MACE Registers */ -#define MACE_RCVFIFO 0 -#define MACE_XMTFIFO 1 -#define MACE_XMTFC 2 -#define MACE_XMTFS 3 -#define MACE_XMTRC 4 -#define MACE_RCVFC 5 -#define MACE_RCVFS 6 -#define MACE_FIFOFC 7 -#define MACE_IR 8 -#define MACE_IMR 9 -#define MACE_PR 10 -#define MACE_BIUCC 11 -#define MACE_FIFOCC 12 -#define MACE_MACCC 13 -#define MACE_PLSCC 14 -#define MACE_PHYCC 15 -#define MACE_CHIPIDL 16 -#define MACE_CHIPIDH 17 -#define MACE_IAC 18 -/* Reserved */ -#define MACE_LADRF 20 -#define MACE_PADR 21 -/* Reserved */ -/* Reserved */ -#define MACE_MPC 24 -/* Reserved */ -#define MACE_RNTPC 26 -#define MACE_RCVCC 27 -/* Reserved */ -#define MACE_UTR 29 -#define MACE_RTR1 30 -#define MACE_RTR2 31 - -/* MACE Bit Masks */ -#define MACE_XMTRC_EXDEF 0x80 -#define MACE_XMTRC_XMTRC 0x0F - -#define MACE_XMTFS_XMTSV 0x80 -#define MACE_XMTFS_UFLO 0x40 -#define MACE_XMTFS_LCOL 0x20 -#define MACE_XMTFS_MORE 0x10 -#define MACE_XMTFS_ONE 0x08 -#define MACE_XMTFS_DEFER 0x04 -#define MACE_XMTFS_LCAR 0x02 -#define MACE_XMTFS_RTRY 0x01 - -#define MACE_RCVFS_RCVSTS 0xF000 -#define MACE_RCVFS_OFLO 0x8000 -#define MACE_RCVFS_CLSN 0x4000 -#define MACE_RCVFS_FRAM 0x2000 -#define MACE_RCVFS_FCS 0x1000 - -#define MACE_FIFOFC_RCVFC 0xF0 -#define MACE_FIFOFC_XMTFC 0x0F - -#define MACE_IR_JAB 0x80 -#define MACE_IR_BABL 0x40 -#define MACE_IR_CERR 0x20 -#define MACE_IR_RCVCCO 0x10 -#define MACE_IR_RNTPCO 0x08 -#define MACE_IR_MPCO 0x04 -#define MACE_IR_RCVINT 0x02 -#define MACE_IR_XMTINT 0x01 - -#define MACE_MACCC_PROM 0x80 -#define MACE_MACCC_DXMT2PD 0x40 -#define MACE_MACCC_EMBA 0x20 -#define MACE_MACCC_RESERVED 0x10 -#define MACE_MACCC_DRCVPA 0x08 -#define MACE_MACCC_DRCVBC 0x04 -#define MACE_MACCC_ENXMT 0x02 -#define MACE_MACCC_ENRCV 0x01 - -#define MACE_PHYCC_LNKFL 0x80 -#define MACE_PHYCC_DLNKTST 0x40 -#define MACE_PHYCC_REVPOL 0x20 -#define MACE_PHYCC_DAPC 0x10 -#define MACE_PHYCC_LRT 0x08 -#define MACE_PHYCC_ASEL 0x04 -#define MACE_PHYCC_RWAKE 0x02 -#define MACE_PHYCC_AWAKE 0x01 - -#define MACE_IAC_ADDRCHG 0x80 -#define MACE_IAC_PHYADDR 0x04 -#define MACE_IAC_LOGADDR 0x02 - -#define MACE_UTR_RTRE 0x80 -#define MACE_UTR_RTRD 0x40 -#define MACE_UTR_RPA 0x20 -#define MACE_UTR_FCOLL 0x10 -#define MACE_UTR_RCVFCSE 0x08 -#define MACE_UTR_LOOP_INCL_MENDEC 0x06 -#define MACE_UTR_LOOP_NO_MENDEC 0x04 -#define MACE_UTR_LOOP_EXTERNAL 0x02 -#define MACE_UTR_LOOP_NONE 0x00 -#define MACE_UTR_RESERVED 0x01 - -/* Switch MACE register bank (only 0 and 1 are valid) */ -#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK) - -#define MACE_IMR_DEFAULT \ - (0xFF - \ - ( \ - MACE_IR_CERR | \ - MACE_IR_RCVCCO | \ - MACE_IR_RNTPCO | \ - MACE_IR_MPCO | \ - MACE_IR_RCVINT | \ - MACE_IR_XMTINT \ - ) \ - ) -#undef MACE_IMR_DEFAULT -#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */ - -#define TX_TIMEOUT ((400*HZ)/1000) - -/* ---------------------------------------------------------------------------- -Type Definitions ----------------------------------------------------------------------------- */ - -typedef struct _mace_statistics { - /* MACE_XMTFS */ - int xmtsv; - int uflo; - int lcol; - int more; - int one; - int defer; - int lcar; - int rtry; - - /* MACE_XMTRC */ - int exdef; - int xmtrc; - - /* RFS1--Receive Status (RCVSTS) */ - int oflo; - int clsn; - int fram; - int fcs; - - /* RFS2--Runt Packet Count (RNTPC) */ - int rfs_rntpc; - - /* RFS3--Receive Collision Count (RCVCC) */ - int rfs_rcvcc; - - /* MACE_IR */ - int jab; - int babl; - int cerr; - int rcvcco; - int rntpco; - int mpco; - - /* MACE_MPC */ - int mpc; - - /* MACE_RNTPC */ - int rntpc; - - /* MACE_RCVCC */ - int rcvcc; -} mace_statistics; - -typedef struct _mace_private { - struct pcmcia_device *p_dev; - mace_statistics mace_stats; /* MACE chip statistics counters */ - - /* restore_multicast_list() state variables */ - int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */ - int multicast_num_addrs; - - char tx_free_frames; /* Number of free transmit frame buffers */ - char tx_irq_disabled; /* MACE TX interrupt disabled */ - - spinlock_t bank_lock; /* Must be held if you step off bank 0 */ -} mace_private; - -/* ---------------------------------------------------------------------------- -Private Global Variables ----------------------------------------------------------------------------- */ - -static const char *if_names[]={ - "Auto", "10baseT", "BNC", -}; - -/* ---------------------------------------------------------------------------- -Parameters - These are the parameters that can be set during loading with - 'insmod'. ----------------------------------------------------------------------------- */ - -MODULE_DESCRIPTION("New Media PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */ -INT_MODULE_PARM(if_port, 0); - - -/* ---------------------------------------------------------------------------- -Function Prototypes ----------------------------------------------------------------------------- */ - -static int nmclan_config(struct pcmcia_device *link); -static void nmclan_release(struct pcmcia_device *link); - -static void nmclan_reset(struct net_device *dev); -static int mace_config(struct net_device *dev, struct ifmap *map); -static int mace_open(struct net_device *dev); -static int mace_close(struct net_device *dev); -static netdev_tx_t mace_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue); -static irqreturn_t mace_interrupt(int irq, void *dev_id); -static struct net_device_stats *mace_get_stats(struct net_device *dev); -static int mace_rx(struct net_device *dev, unsigned char RxCnt); -static void restore_multicast_list(struct net_device *dev); -static void set_multicast_list(struct net_device *dev); -static const struct ethtool_ops netdev_ethtool_ops; - - -static void nmclan_detach(struct pcmcia_device *p_dev); - -static const struct net_device_ops mace_netdev_ops = { - .ndo_open = mace_open, - .ndo_stop = mace_close, - .ndo_start_xmit = mace_start_xmit, - .ndo_tx_timeout = mace_tx_timeout, - .ndo_set_config = mace_config, - .ndo_get_stats = mace_get_stats, - .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int nmclan_probe(struct pcmcia_device *link) -{ - mace_private *lp; - struct net_device *dev; - - dev_dbg(&link->dev, "nmclan_attach()\n"); - - /* Create new ethernet device */ - dev = alloc_etherdev(sizeof(mace_private)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - lp->p_dev = link; - link->priv = dev; - - spin_lock_init(&lp->bank_lock); - link->resource[0]->end = 32; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - link->config_flags |= CONF_ENABLE_IRQ; - link->config_index = 1; - link->config_regs = PRESENT_OPTION; - - lp->tx_free_frames=AM2150_MAX_TX_FRAMES; - - dev->netdev_ops = &mace_netdev_ops; - dev->ethtool_ops = &netdev_ethtool_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - return nmclan_config(link); -} /* nmclan_attach */ - -static void nmclan_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "nmclan_detach\n"); - - unregister_netdev(dev); - - nmclan_release(link); - - free_netdev(dev); -} /* nmclan_detach */ - -/* ---------------------------------------------------------------------------- -mace_read - Reads a MACE register. This is bank independent; however, the - caller must ensure that this call is not interruptable. We are - assuming that during normal operation, the MACE is always in - bank 0. ----------------------------------------------------------------------------- */ -static int mace_read(mace_private *lp, unsigned int ioaddr, int reg) -{ - int data = 0xFF; - unsigned long flags; - - switch (reg >> 4) { - case 0: /* register 0-15 */ - data = inb(ioaddr + AM2150_MACE_BASE + reg); - break; - case 1: /* register 16-31 */ - spin_lock_irqsave(&lp->bank_lock, flags); - MACEBANK(1); - data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); - MACEBANK(0); - spin_unlock_irqrestore(&lp->bank_lock, flags); - break; - } - return data & 0xFF; -} /* mace_read */ - -/* ---------------------------------------------------------------------------- -mace_write - Writes to a MACE register. This is bank independent; however, - the caller must ensure that this call is not interruptable. We - are assuming that during normal operation, the MACE is always in - bank 0. ----------------------------------------------------------------------------- */ -static void mace_write(mace_private *lp, unsigned int ioaddr, int reg, - int data) -{ - unsigned long flags; - - switch (reg >> 4) { - case 0: /* register 0-15 */ - outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg); - break; - case 1: /* register 16-31 */ - spin_lock_irqsave(&lp->bank_lock, flags); - MACEBANK(1); - outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F)); - MACEBANK(0); - spin_unlock_irqrestore(&lp->bank_lock, flags); - break; - } -} /* mace_write */ - -/* ---------------------------------------------------------------------------- -mace_init - Resets the MACE chip. ----------------------------------------------------------------------------- */ -static int mace_init(mace_private *lp, unsigned int ioaddr, - const char *enet_addr) -{ - int i; - int ct = 0; - - /* MACE Software reset */ - mace_write(lp, ioaddr, MACE_BIUCC, 1); - while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) { - /* Wait for reset bit to be cleared automatically after <= 200ns */; - if(++ct > 500) - { - pr_err("reset failed, card removed?\n"); - return -1; - } - udelay(1); - } - mace_write(lp, ioaddr, MACE_BIUCC, 0); - - /* The Am2150 requires that the MACE FIFOs operate in burst mode. */ - mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F); - - mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */ - mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */ - - /* - * Bit 2-1 PORTSEL[1-0] Port Select. - * 00 AUI/10Base-2 - * 01 10Base-T - * 10 DAI Port (reserved in Am2150) - * 11 GPSI - * For this card, only the first two are valid. - * So, PLSCC should be set to - * 0x00 for 10Base-2 - * 0x02 for 10Base-T - * Or just set ASEL in PHYCC below! - */ - switch (if_port) { - case 1: - mace_write(lp, ioaddr, MACE_PLSCC, 0x02); - break; - case 2: - mace_write(lp, ioaddr, MACE_PLSCC, 0x00); - break; - default: - mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4); - /* ASEL Auto Select. When set, the PORTSEL[1-0] bits are overridden, - and the MACE device will automatically select the operating media - interface port. */ - break; - } - - mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR); - /* Poll ADDRCHG bit */ - ct = 0; - while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) - { - if(++ ct > 500) - { - pr_err("ADDRCHG timeout, card removed?\n"); - return -1; - } - } - /* Set PADR register */ - for (i = 0; i < ETH_ALEN; i++) - mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]); - - /* MAC Configuration Control Register should be written last */ - /* Let set_multicast_list set this. */ - /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */ - mace_write(lp, ioaddr, MACE_MACCC, 0x00); - return 0; -} /* mace_init */ - -static int nmclan_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - mace_private *lp = netdev_priv(dev); - u8 *buf; - size_t len; - int i, ret; - unsigned int ioaddr; - - dev_dbg(&link->dev, "nmclan_config\n"); - - link->io_lines = 5; - ret = pcmcia_request_io(link); - if (ret) - goto failed; - ret = pcmcia_request_irq(link, mace_interrupt); - if (ret) - goto failed; - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - ioaddr = dev->base_addr; - - /* Read the ethernet address from the CIS. */ - len = pcmcia_get_tuple(link, 0x80, &buf); - if (!buf || len < ETH_ALEN) { - kfree(buf); - goto failed; - } - eth_hw_addr_set(dev, buf); - kfree(buf); - - /* Verify configuration by reading the MACE ID. */ - { - char sig[2]; - - sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL); - sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH); - if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) { - dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n", - sig[0], sig[1]); - } else { - pr_notice("mace id not found: %x %x should be 0x40 0x?9\n", - sig[0], sig[1]); - goto failed; - } - } - - if(mace_init(lp, ioaddr, dev->dev_addr) == -1) - goto failed; - - /* The if_port symbol can be set when the module is loaded */ - if (if_port <= 2) - dev->if_port = if_port; - else - pr_notice("invalid if_port requested\n"); - - SET_NETDEV_DEV(dev, &link->dev); - - i = register_netdev(dev); - if (i != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - netdev_info(dev, "nmclan: port %#3lx, irq %d, %s port, hw_addr %pM\n", - dev->base_addr, dev->irq, if_names[dev->if_port], dev->dev_addr); - return 0; - -failed: - nmclan_release(link); - return -ENODEV; -} /* nmclan_config */ - -static void nmclan_release(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "nmclan_release\n"); - pcmcia_disable_device(link); -} - -static int nmclan_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int nmclan_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - nmclan_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - - -/* ---------------------------------------------------------------------------- -nmclan_reset - Reset and restore all of the Xilinx and MACE registers. ----------------------------------------------------------------------------- */ -static void nmclan_reset(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - -#if RESET_XILINX - struct pcmcia_device *link = &lp->link; - u8 OrigCorValue; - - /* Save original COR value */ - pcmcia_read_config_byte(link, CISREG_COR, &OrigCorValue); - - /* Reset Xilinx */ - dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%x, resetting...\n", - OrigCorValue); - pcmcia_write_config_byte(link, CISREG_COR, COR_SOFT_RESET); - /* Need to wait for 20 ms for PCMCIA to finish reset. */ - - /* Restore original COR configuration index */ - pcmcia_write_config_byte(link, CISREG_COR, - (COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK))); - /* Xilinx is now completely reset along with the MACE chip. */ - lp->tx_free_frames=AM2150_MAX_TX_FRAMES; - -#endif /* #if RESET_XILINX */ - - /* Xilinx is now completely reset along with the MACE chip. */ - lp->tx_free_frames=AM2150_MAX_TX_FRAMES; - - /* Reinitialize the MACE chip for operation. */ - mace_init(lp, dev->base_addr, dev->dev_addr); - mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT); - - /* Restore the multicast list and enable TX and RX. */ - restore_multicast_list(dev); -} /* nmclan_reset */ - -/* ---------------------------------------------------------------------------- -mace_config - [Someone tell me what this is supposed to do? Is if_port a defined - standard? If so, there should be defines to indicate 1=10Base-T, - 2=10Base-2, etc. including limited automatic detection.] ----------------------------------------------------------------------------- */ -static int mace_config(struct net_device *dev, struct ifmap *map) -{ - if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { - if (map->port <= 2) { - WRITE_ONCE(dev->if_port, map->port); - netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); - } else - return -EINVAL; - } - return 0; -} /* mace_config */ - -/* ---------------------------------------------------------------------------- -mace_open - Open device driver. ----------------------------------------------------------------------------- */ -static int mace_open(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - mace_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - - MACEBANK(0); - - netif_start_queue(dev); - nmclan_reset(dev); - - return 0; /* Always succeed */ -} /* mace_open */ - -/* ---------------------------------------------------------------------------- -mace_close - Closes device driver. ----------------------------------------------------------------------------- */ -static int mace_close(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - mace_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name); - - /* Mask off all interrupts from the MACE chip. */ - outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR); - - link->open--; - netif_stop_queue(dev); - - return 0; -} /* mace_close */ - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - snprintf(info->bus_info, sizeof(info->bus_info), - "PCMCIA 0x%lx", dev->base_addr); -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, -}; - -/* ---------------------------------------------------------------------------- -mace_start_xmit - This routine begins the packet transmit function. When completed, - it will generate a transmit interrupt. - - According to /usr/src/linux/net/inet/dev.c, if _start_xmit - returns 0, the "packet is now solely the responsibility of the - driver." If _start_xmit returns non-zero, the "transmission - failed, put skb back into a list." ----------------------------------------------------------------------------- */ - -static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - mace_private *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - netdev_notice(dev, "transmit timed out -- "); -#if RESET_ON_TIMEOUT - pr_cont("resetting card\n"); - pcmcia_reset_card(link->socket); -#else /* #if RESET_ON_TIMEOUT */ - pr_cont("NOT resetting card\n"); -#endif /* #if RESET_ON_TIMEOUT */ - netif_trans_update(dev); /* prevent tx timeout */ - netif_wake_queue(dev); -} - -static netdev_tx_t mace_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - netif_stop_queue(dev); - - pr_debug("%s: mace_start_xmit(length = %ld) called.\n", - dev->name, (long)skb->len); - -#if (!TX_INTERRUPTABLE) - /* Disable MACE TX interrupts. */ - outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT, - ioaddr + AM2150_MACE_BASE + MACE_IMR); - lp->tx_irq_disabled=1; -#endif /* #if (!TX_INTERRUPTABLE) */ - - { - /* This block must not be interrupted by another transmit request! - mace_tx_timeout will take care of timer-based retransmissions from - the upper layers. The interrupt handler is guaranteed never to - service a transmit interrupt while we are in here. - */ - - dev->stats.tx_bytes += skb->len; - lp->tx_free_frames--; - - /* WARNING: Write the _exact_ number of bytes written in the header! */ - /* Put out the word header [must be an outw()] . . . */ - outw(skb->len, ioaddr + AM2150_XMT); - /* . . . and the packet [may be any combination of outw() and outb()] */ - outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1); - if (skb->len & 1) { - /* Odd byte transfer */ - outb(skb->data[skb->len-1], ioaddr + AM2150_XMT); - } - -#if MULTI_TX - if (lp->tx_free_frames > 0) - netif_start_queue(dev); -#endif /* #if MULTI_TX */ - } - -#if (!TX_INTERRUPTABLE) - /* Re-enable MACE TX interrupts. */ - lp->tx_irq_disabled=0; - outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR); -#endif /* #if (!TX_INTERRUPTABLE) */ - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} /* mace_start_xmit */ - -/* ---------------------------------------------------------------------------- -mace_interrupt - The interrupt handler. ----------------------------------------------------------------------------- */ -static irqreturn_t mace_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - mace_private *lp = netdev_priv(dev); - unsigned int ioaddr; - int status; - int IntrCnt = MACE_MAX_IR_ITERATIONS; - - if (!dev) { - pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n", - irq); - return IRQ_NONE; - } - - ioaddr = dev->base_addr; - - if (lp->tx_irq_disabled) { - const char *msg; - if (lp->tx_irq_disabled) - msg = "Interrupt with tx_irq_disabled"; - else - msg = "Re-entering the interrupt handler"; - netdev_notice(dev, "%s [isr=%02X, imr=%02X]\n", - msg, - inb(ioaddr + AM2150_MACE_BASE + MACE_IR), - inb(ioaddr + AM2150_MACE_BASE + MACE_IMR)); - /* WARNING: MACE_IR has been read! */ - return IRQ_NONE; - } - - if (!netif_device_present(dev)) { - netdev_dbg(dev, "interrupt from dead card\n"); - return IRQ_NONE; - } - - do { - /* WARNING: MACE_IR is a READ/CLEAR port! */ - status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR); - if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS) - return IRQ_NONE; - - pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status); - - if (status & MACE_IR_RCVINT) { - mace_rx(dev, MACE_MAX_RX_ITERATIONS); - } - - if (status & MACE_IR_XMTINT) { - unsigned char fifofc; - unsigned char xmtrc; - unsigned char xmtfs; - - fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC); - if ((fifofc & MACE_FIFOFC_XMTFC)==0) { - dev->stats.tx_errors++; - outb(0xFF, ioaddr + AM2150_XMT_SKIP); - } - - /* Transmit Retry Count (XMTRC, reg 4) */ - xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC); - if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++; - lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC); - - if ( - (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) & - MACE_XMTFS_XMTSV /* Transmit Status Valid */ - ) { - lp->mace_stats.xmtsv++; - - if (xmtfs & ~MACE_XMTFS_XMTSV) { - if (xmtfs & MACE_XMTFS_UFLO) { - /* Underflow. Indicates that the Transmit FIFO emptied before - the end of frame was reached. */ - lp->mace_stats.uflo++; - } - if (xmtfs & MACE_XMTFS_LCOL) { - /* Late Collision */ - lp->mace_stats.lcol++; - } - if (xmtfs & MACE_XMTFS_MORE) { - /* MORE than one retry was needed */ - lp->mace_stats.more++; - } - if (xmtfs & MACE_XMTFS_ONE) { - /* Exactly ONE retry occurred */ - lp->mace_stats.one++; - } - if (xmtfs & MACE_XMTFS_DEFER) { - /* Transmission was defered */ - lp->mace_stats.defer++; - } - if (xmtfs & MACE_XMTFS_LCAR) { - /* Loss of carrier */ - lp->mace_stats.lcar++; - } - if (xmtfs & MACE_XMTFS_RTRY) { - /* Retry error: transmit aborted after 16 attempts */ - lp->mace_stats.rtry++; - } - } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */ - - } /* if (xmtfs & MACE_XMTFS_XMTSV) */ - - dev->stats.tx_packets++; - lp->tx_free_frames++; - netif_wake_queue(dev); - } /* if (status & MACE_IR_XMTINT) */ - - if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) { - if (status & MACE_IR_JAB) { - /* Jabber Error. Excessive transmit duration (20-150ms). */ - lp->mace_stats.jab++; - } - if (status & MACE_IR_BABL) { - /* Babble Error. >1518 bytes transmitted. */ - lp->mace_stats.babl++; - } - if (status & MACE_IR_CERR) { - /* Collision Error. CERR indicates the absence of the - Signal Quality Error Test message after a packet - transmission. */ - lp->mace_stats.cerr++; - } - if (status & MACE_IR_RCVCCO) { - /* Receive Collision Count Overflow; */ - lp->mace_stats.rcvcco++; - } - if (status & MACE_IR_RNTPCO) { - /* Runt Packet Count Overflow */ - lp->mace_stats.rntpco++; - } - if (status & MACE_IR_MPCO) { - /* Missed Packet Count Overflow */ - lp->mace_stats.mpco++; - } - } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */ - - } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt)); - - return IRQ_HANDLED; -} /* mace_interrupt */ - -/* ---------------------------------------------------------------------------- -mace_rx - Receives packets. ----------------------------------------------------------------------------- */ -static int mace_rx(struct net_device *dev, unsigned char RxCnt) -{ - mace_private *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - unsigned char rx_framecnt; - unsigned short rx_status; - - while ( - ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) && - (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */ - (RxCnt--) - ) { - rx_status = inw(ioaddr + AM2150_RCV); - - pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status" - " 0x%X.\n", dev->name, rx_framecnt, rx_status); - - if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */ - dev->stats.rx_errors++; - if (rx_status & MACE_RCVFS_OFLO) { - lp->mace_stats.oflo++; - } - if (rx_status & MACE_RCVFS_CLSN) { - lp->mace_stats.clsn++; - } - if (rx_status & MACE_RCVFS_FRAM) { - lp->mace_stats.fram++; - } - if (rx_status & MACE_RCVFS_FCS) { - lp->mace_stats.fcs++; - } - } else { - short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4; - /* Auto Strip is off, always subtract 4 */ - struct sk_buff *skb; - - lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV); - /* runt packet count */ - lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV); - /* rcv collision count */ - - pr_debug(" receiving packet size 0x%X rx_status" - " 0x%X.\n", pkt_len, rx_status); - - skb = netdev_alloc_skb(dev, pkt_len + 2); - - if (skb) { - skb_reserve(skb, 2); - insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1); - if (pkt_len & 1) - *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV); - skb->protocol = eth_type_trans(skb, dev); - - netif_rx(skb); /* Send the packet to the upper (protocol) layers. */ - - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ - continue; - } else { - pr_debug("%s: couldn't allocate a sk_buff of size" - " %d.\n", dev->name, pkt_len); - dev->stats.rx_dropped++; - } - } - outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */ - } /* while */ - - return 0; -} /* mace_rx */ - -/* ---------------------------------------------------------------------------- -pr_linux_stats ----------------------------------------------------------------------------- */ -static void pr_linux_stats(struct net_device_stats *pstats) -{ - pr_debug("pr_linux_stats\n"); - pr_debug(" rx_packets=%-7ld tx_packets=%ld\n", - (long)pstats->rx_packets, (long)pstats->tx_packets); - pr_debug(" rx_errors=%-7ld tx_errors=%ld\n", - (long)pstats->rx_errors, (long)pstats->tx_errors); - pr_debug(" rx_dropped=%-7ld tx_dropped=%ld\n", - (long)pstats->rx_dropped, (long)pstats->tx_dropped); - pr_debug(" multicast=%-7ld collisions=%ld\n", - (long)pstats->multicast, (long)pstats->collisions); - - pr_debug(" rx_length_errors=%-7ld rx_over_errors=%ld\n", - (long)pstats->rx_length_errors, (long)pstats->rx_over_errors); - pr_debug(" rx_crc_errors=%-7ld rx_frame_errors=%ld\n", - (long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors); - pr_debug(" rx_fifo_errors=%-7ld rx_missed_errors=%ld\n", - (long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors); - - pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n", - (long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors); - pr_debug(" tx_fifo_errors=%-7ld tx_heartbeat_errors=%ld\n", - (long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors); - pr_debug(" tx_window_errors=%ld\n", - (long)pstats->tx_window_errors); -} /* pr_linux_stats */ - -/* ---------------------------------------------------------------------------- -pr_mace_stats ----------------------------------------------------------------------------- */ -static void pr_mace_stats(mace_statistics *pstats) -{ - pr_debug("pr_mace_stats\n"); - - pr_debug(" xmtsv=%-7d uflo=%d\n", - pstats->xmtsv, pstats->uflo); - pr_debug(" lcol=%-7d more=%d\n", - pstats->lcol, pstats->more); - pr_debug(" one=%-7d defer=%d\n", - pstats->one, pstats->defer); - pr_debug(" lcar=%-7d rtry=%d\n", - pstats->lcar, pstats->rtry); - - /* MACE_XMTRC */ - pr_debug(" exdef=%-7d xmtrc=%d\n", - pstats->exdef, pstats->xmtrc); - - /* RFS1--Receive Status (RCVSTS) */ - pr_debug(" oflo=%-7d clsn=%d\n", - pstats->oflo, pstats->clsn); - pr_debug(" fram=%-7d fcs=%d\n", - pstats->fram, pstats->fcs); - - /* RFS2--Runt Packet Count (RNTPC) */ - /* RFS3--Receive Collision Count (RCVCC) */ - pr_debug(" rfs_rntpc=%-7d rfs_rcvcc=%d\n", - pstats->rfs_rntpc, pstats->rfs_rcvcc); - - /* MACE_IR */ - pr_debug(" jab=%-7d babl=%d\n", - pstats->jab, pstats->babl); - pr_debug(" cerr=%-7d rcvcco=%d\n", - pstats->cerr, pstats->rcvcco); - pr_debug(" rntpco=%-7d mpco=%d\n", - pstats->rntpco, pstats->mpco); - - /* MACE_MPC */ - pr_debug(" mpc=%d\n", pstats->mpc); - - /* MACE_RNTPC */ - pr_debug(" rntpc=%d\n", pstats->rntpc); - - /* MACE_RCVCC */ - pr_debug(" rcvcc=%d\n", pstats->rcvcc); - -} /* pr_mace_stats */ - -/* ---------------------------------------------------------------------------- -update_stats - Update statistics. We change to register window 1, so this - should be run single-threaded if the device is active. This is - expected to be a rare operation, and it's simpler for the rest - of the driver to assume that window 0 is always valid rather - than use a special window-state variable. - - oflo & uflo should _never_ occur since it would mean the Xilinx - was not able to transfer data between the MACE FIFO and the - card's SRAM fast enough. If this happens, something is - seriously wrong with the hardware. ----------------------------------------------------------------------------- */ -static void update_stats(unsigned int ioaddr, struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - - lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC); - lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC); - lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC); - /* At this point, mace_stats is fully updated for this call. - We may now update the netdev stats. */ - - /* The MACE has no equivalent for netdev stats field which are commented - out. */ - - /* dev->stats.multicast; */ - dev->stats.collisions = - lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc; - /* Collision: The MACE may retry sending a packet 15 times - before giving up. The retry count is in XMTRC. - Does each retry constitute a collision? - If so, why doesn't the RCVCC record these collisions? */ - - /* detailed rx_errors: */ - dev->stats.rx_length_errors = - lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc; - /* dev->stats.rx_over_errors */ - dev->stats.rx_crc_errors = lp->mace_stats.fcs; - dev->stats.rx_frame_errors = lp->mace_stats.fram; - dev->stats.rx_fifo_errors = lp->mace_stats.oflo; - dev->stats.rx_missed_errors = - lp->mace_stats.mpco * 256 + lp->mace_stats.mpc; - - /* detailed tx_errors */ - dev->stats.tx_aborted_errors = lp->mace_stats.rtry; - dev->stats.tx_carrier_errors = lp->mace_stats.lcar; - /* LCAR usually results from bad cabling. */ - dev->stats.tx_fifo_errors = lp->mace_stats.uflo; - dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr; - /* dev->stats.tx_window_errors; */ -} /* update_stats */ - -/* ---------------------------------------------------------------------------- -mace_get_stats - Gathers ethernet statistics from the MACE chip. ----------------------------------------------------------------------------- */ -static struct net_device_stats *mace_get_stats(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - - update_stats(dev->base_addr, dev); - - pr_debug("%s: updating the statistics.\n", dev->name); - pr_linux_stats(&dev->stats); - pr_mace_stats(&lp->mace_stats); - - return &dev->stats; -} /* net_device_stats */ - -/* ---------------------------------------------------------------------------- -updateCRC - Modified from Am79C90 data sheet. ----------------------------------------------------------------------------- */ - -#ifdef BROKEN_MULTICAST - -static void updateCRC(int *CRC, int bit) -{ - static const int poly[]={ - 1,1,1,0, 1,1,0,1, - 1,0,1,1, 1,0,0,0, - 1,0,0,0, 0,0,1,1, - 0,0,1,0, 0,0,0,0 - }; /* CRC polynomial. poly[n] = coefficient of the x**n term of the - CRC generator polynomial. */ - - int j; - - /* shift CRC and control bit (CRC[32]) */ - for (j = 32; j > 0; j--) - CRC[j] = CRC[j-1]; - CRC[0] = 0; - - /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */ - if (bit ^ CRC[32]) - for (j = 0; j < 32; j++) - CRC[j] ^= poly[j]; -} /* updateCRC */ - -/* ---------------------------------------------------------------------------- -BuildLAF - Build logical address filter. - Modified from Am79C90 data sheet. - -Input - ladrf: logical address filter (contents initialized to 0) - adr: ethernet address ----------------------------------------------------------------------------- */ -static void BuildLAF(int *ladrf, int *adr) -{ - int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */ - - int i, byte; /* temporary array indices */ - int hashcode; /* the output object */ - - CRC[32]=0; - - for (byte = 0; byte < 6; byte++) - for (i = 0; i < 8; i++) - updateCRC(CRC, (adr[byte] >> i) & 1); - - hashcode = 0; - for (i = 0; i < 6; i++) - hashcode = (hashcode << 1) + CRC[i]; - - byte = hashcode >> 3; - ladrf[byte] |= (1 << (hashcode & 7)); - -#ifdef PCMCIA_DEBUG - if (0) - printk(KERN_DEBUG " adr =%pM\n", adr); - printk(KERN_DEBUG " hashcode = %d(decimal), ladrf[0:63] =", hashcode); - for (i = 0; i < 8; i++) - pr_cont(" %02X", ladrf[i]); - pr_cont("\n"); -#endif -} /* BuildLAF */ - -/* ---------------------------------------------------------------------------- -restore_multicast_list - Restores the multicast filter for MACE chip to the last - set_multicast_list() call. - -Input - multicast_num_addrs - multicast_ladrf[] ----------------------------------------------------------------------------- */ -static void restore_multicast_list(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - int num_addrs = lp->multicast_num_addrs; - int *ladrf = lp->multicast_ladrf; - unsigned int ioaddr = dev->base_addr; - int i; - - pr_debug("%s: restoring Rx mode to %d addresses.\n", - dev->name, num_addrs); - - if (num_addrs > 0) { - - pr_debug("Attempt to restore multicast list detected.\n"); - - mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR); - /* Poll ADDRCHG bit */ - while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG) - ; - /* Set LADRF register */ - for (i = 0; i < MACE_LADRF_LEN; i++) - mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]); - - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); - - } else if (num_addrs < 0) { - - /* Promiscuous mode: receive all packets */ - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, - MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV - ); - - } else { - - /* Normal mode */ - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); - - } -} /* restore_multicast_list */ - -/* ---------------------------------------------------------------------------- -set_multicast_list - Set or clear the multicast filter for this adaptor. - -Input - num_addrs == -1 Promiscuous mode, receive all packets - num_addrs == 0 Normal mode, clear multicast list - num_addrs > 0 Multicast mode, receive normal and MC packets, and do - best-effort filtering. -Output - multicast_num_addrs - multicast_ladrf[] ----------------------------------------------------------------------------- */ - -static void set_multicast_list(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - int adr[ETH_ALEN] = {0}; /* Ethernet address */ - struct netdev_hw_addr *ha; - -#ifdef PCMCIA_DEBUG - { - static int old; - if (netdev_mc_count(dev) != old) { - old = netdev_mc_count(dev); - pr_debug("%s: setting Rx mode to %d addresses.\n", - dev->name, old); - } - } -#endif - - /* Set multicast_num_addrs. */ - lp->multicast_num_addrs = netdev_mc_count(dev); - - /* Set multicast_ladrf. */ - if (num_addrs > 0) { - /* Calculate multicast logical address filter */ - memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN); - netdev_for_each_mc_addr(ha, dev) { - memcpy(adr, ha->addr, ETH_ALEN); - BuildLAF(lp->multicast_ladrf, adr); - } - } - - restore_multicast_list(dev); - -} /* set_multicast_list */ - -#endif /* BROKEN_MULTICAST */ - -static void restore_multicast_list(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - mace_private *lp = netdev_priv(dev); - - pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name, - lp->multicast_num_addrs); - - if (dev->flags & IFF_PROMISC) { - /* Promiscuous mode: receive all packets */ - mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, - MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV - ); - } else { - /* Normal mode */ - mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL); - mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); - } -} /* restore_multicast_list */ - -static void set_multicast_list(struct net_device *dev) -{ - mace_private *lp = netdev_priv(dev); - -#ifdef PCMCIA_DEBUG - { - static int old; - if (netdev_mc_count(dev) != old) { - old = netdev_mc_count(dev); - pr_debug("%s: setting Rx mode to %d addresses.\n", - dev->name, old); - } - } -#endif - - lp->multicast_num_addrs = netdev_mc_count(dev); - restore_multicast_list(dev); - -} /* set_multicast_list */ - -static const struct pcmcia_device_id nmclan_ids[] = { - PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941), - PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, nmclan_ids); - -static struct pcmcia_driver nmclan_cs_driver = { - .owner = THIS_MODULE, - .name = "nmclan_cs", - .probe = nmclan_probe, - .remove = nmclan_detach, - .id_table = nmclan_ids, - .suspend = nmclan_suspend, - .resume = nmclan_resume, -}; -module_pcmcia_driver(nmclan_cs_driver); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 60b7e53206d1..3d3b09010d48 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -135,11 +135,11 @@ */ #define XGBE_TSTAMP_SSINC 20 #define XGBE_TSTAMP_SNSINC 0 -#define XGBE_PTP_ACT_CLK_FREQ 500000000 +#define XGBE_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_TSTAMP_SSINC) #define XGBE_V2_TSTAMP_SSINC 0xA #define XGBE_V2_TSTAMP_SNSINC 0 -#define XGBE_V2_PTP_ACT_CLK_FREQ 1000000000 +#define XGBE_V2_PTP_ACT_CLK_FREQ (NSEC_PER_SEC / XGBE_V2_TSTAMP_SSINC) /* Define maximum supported values */ #define XGBE_MAX_PPS_OUT 4 diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index b854b6b42d77..2926e1e59941 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -910,7 +910,9 @@ static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata, return -ENXIO; } - return of_mdiobus_register(mdio, mdio_np); + ret = of_mdiobus_register(mdio, mdio_np); + of_node_put(mdio_np); + return ret; } /* Mask out all PHYs from auto probing. */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c index e9e38af680c3..39e1b606a75a 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_pci_func.c @@ -371,7 +371,7 @@ static void aq_pci_shutdown(struct pci_dev *pdev) pci_disable_device(pdev); if (system_state == SYSTEM_POWER_OFF) { - pci_wake_from_d3(pdev, false); + pci_wake_from_d3(pdev, self->aq_hw->aq_nic_cfg->wol); pci_set_power_state(pdev, PCI_D3hot); } } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c index 1c14c5fe8d61..68b74eb2c3a2 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c @@ -74,6 +74,13 @@ static int bnge_func_qcaps(struct bnge_dev *bd) return rc; } + return 0; +} + +static int bnge_func_qrcaps_qcfg(struct bnge_dev *bd) +{ + int rc; + rc = bnge_hwrm_func_resc_qcaps(bd); if (rc) { dev_err(bd->dev, "query resc caps failure rc: %d\n", rc); @@ -133,23 +140,28 @@ static int bnge_fw_register_dev(struct bnge_dev *bd) bnge_hwrm_fw_set_time(bd); - rc = bnge_hwrm_func_drv_rgtr(bd); + /* Get the resources and configuration from firmware */ + rc = bnge_func_qcaps(bd); if (rc) { - dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc); + dev_err(bd->dev, "Failed querying caps rc: %d\n", rc); return rc; } rc = bnge_alloc_ctx_mem(bd); if (rc) { dev_err(bd->dev, "Failed to allocate ctx mem rc: %d\n", rc); - goto err_func_unrgtr; + goto err_free_ctx_mem; } - /* Get the resources and configuration from firmware */ - rc = bnge_func_qcaps(bd); + rc = bnge_hwrm_func_drv_rgtr(bd); if (rc) { - dev_err(bd->dev, "Failed initial configuration rc: %d\n", rc); - rc = -ENODEV; + dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc); + goto err_free_ctx_mem; + } + + rc = bnge_func_qrcaps_qcfg(bd); + if (rc) { + dev_err(bd->dev, "Failed querying resources rc: %d\n", rc); goto err_func_unrgtr; } @@ -158,7 +170,9 @@ static int bnge_fw_register_dev(struct bnge_dev *bd) return 0; err_func_unrgtr: - bnge_fw_unregister_dev(bd); + bnge_hwrm_func_drv_unrgtr(bd); +err_free_ctx_mem: + bnge_free_ctx_mem(bd); return rc; } diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c index 94f15e08a88c..b066ee887a09 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_rmem.c @@ -324,7 +324,6 @@ int bnge_alloc_ctx_mem(struct bnge_dev *bd) u32 l2_qps, qp1_qps, max_qps; u32 ena, entries_sp, entries; u32 srqs, max_srqs, min; - u32 num_mr, num_ah; u32 extra_srqs = 0; u32 extra_qps = 0; u32 fast_qpmd_qps; @@ -390,21 +389,6 @@ int bnge_alloc_ctx_mem(struct bnge_dev *bd) if (!bnge_is_roce_en(bd)) goto skip_rdma; - ctxm = &ctx->ctx_arr[BNGE_CTX_MRAV]; - /* 128K extra is needed to accommodate static AH context - * allocation by f/w. - */ - num_mr = min_t(u32, ctxm->max_entries / 2, 1024 * 256); - num_ah = min_t(u32, num_mr, 1024 * 128); - ctxm->split_entry_cnt = BNGE_CTX_MRAV_AV_SPLIT_ENTRY + 1; - if (!ctxm->mrav_av_entries || ctxm->mrav_av_entries > num_ah) - ctxm->mrav_av_entries = num_ah; - - rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, num_mr + num_ah, 2); - if (rc) - return rc; - ena |= FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV; - ctxm = &ctx->ctx_arr[BNGE_CTX_TIM]; rc = bnge_setup_ctxm_pg_tbls(bd, ctxm, l2_qps + qp1_qps + extra_qps, 1); if (rc) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 58cf02bcb98a..008c34cff7b4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -3825,7 +3825,10 @@ static int bnxt_alloc_tpa_info(struct bnxt *bp) if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { if (!bp->max_tpa_v2) return 0; - bp->max_tpa = max_t(u16, bp->max_tpa_v2, MAX_TPA_P5); + bp->max_tpa = min_t(u16, bp->max_tpa_v2, MAX_TPA_P5); + /* Older P5 FW sets max_tpa_v2 low by mistake except NPAR */ + if (bp->max_tpa <= 32 && BNXT_CHIP_P5(bp) && !BNXT_NPAR(bp)) + bp->max_tpa = MAX_TPA_P5; } for (i = 0; i < bp->rx_nr_rings; i++) { @@ -11132,8 +11135,9 @@ static int bnxt_setup_nitroa0_vnic(struct bnxt *bp) return rc; } -static int bnxt_cfg_rx_mode(struct bnxt *); -static bool bnxt_mc_list_updated(struct bnxt *, u32 *); +static int bnxt_cfg_rx_mode(struct bnxt *, struct netdev_hw_addr_list *, bool); +static bool bnxt_mc_list_updated(struct bnxt *, u32 *, + const struct netdev_hw_addr_list *); static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) { @@ -11223,11 +11227,11 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) } else if (bp->dev->flags & IFF_MULTICAST) { u32 mask = 0; - bnxt_mc_list_updated(bp, &mask); + bnxt_mc_list_updated(bp, &mask, &bp->dev->mc); vnic->rx_mask |= mask; } - rc = bnxt_cfg_rx_mode(bp); + rc = bnxt_cfg_rx_mode(bp, &bp->dev->uc, true); if (rc) goto err_out; @@ -13622,17 +13626,17 @@ void bnxt_get_ring_drv_stats(struct bnxt *bp, bnxt_get_one_ring_drv_stats(bp, stats, &bp->bnapi[i]->cp_ring); } -static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask) +static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask, + const struct netdev_hw_addr_list *mc) { struct bnxt_vnic_info *vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT]; - struct net_device *dev = bp->dev; struct netdev_hw_addr *ha; u8 *haddr; int mc_count = 0; bool update = false; int off = 0; - netdev_for_each_mc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, mc) { if (mc_count >= BNXT_MAX_MC_ADDRS) { *rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; vnic->mc_list_count = 0; @@ -13656,17 +13660,17 @@ static bool bnxt_mc_list_updated(struct bnxt *bp, u32 *rx_mask) return update; } -static bool bnxt_uc_list_updated(struct bnxt *bp) +static bool bnxt_uc_list_updated(struct bnxt *bp, + const struct netdev_hw_addr_list *uc) { - struct net_device *dev = bp->dev; struct bnxt_vnic_info *vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT]; struct netdev_hw_addr *ha; int off = 0; - if (netdev_uc_count(dev) != (vnic->uc_filter_count - 1)) + if (netdev_hw_addr_list_count(uc) != (vnic->uc_filter_count - 1)) return true; - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { if (!ether_addr_equal(ha->addr, vnic->uc_list + off)) return true; @@ -13675,7 +13679,9 @@ static bool bnxt_uc_list_updated(struct bnxt *bp) return false; } -static void bnxt_set_rx_mode(struct net_device *dev) +static void bnxt_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct bnxt *bp = netdev_priv(dev); struct bnxt_vnic_info *vnic; @@ -13696,7 +13702,7 @@ static void bnxt_set_rx_mode(struct net_device *dev) if (dev->flags & IFF_PROMISC) mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; - uc_update = bnxt_uc_list_updated(bp); + uc_update = bnxt_uc_list_updated(bp, uc); if (dev->flags & IFF_BROADCAST) mask |= CFA_L2_SET_RX_MASK_REQ_MASK_BCAST; @@ -13704,27 +13710,23 @@ static void bnxt_set_rx_mode(struct net_device *dev) mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST; vnic->mc_list_count = 0; } else if (dev->flags & IFF_MULTICAST) { - mc_update = bnxt_mc_list_updated(bp, &mask); + mc_update = bnxt_mc_list_updated(bp, &mask, mc); } if (mask != vnic->rx_mask || uc_update || mc_update) { vnic->rx_mask = mask; - bnxt_queue_sp_work(bp, BNXT_RX_MASK_SP_EVENT); + bnxt_cfg_rx_mode(bp, uc, uc_update); } } -static int bnxt_cfg_rx_mode(struct bnxt *bp) +static int bnxt_cfg_rx_mode(struct bnxt *bp, struct netdev_hw_addr_list *uc, + bool uc_update) { struct net_device *dev = bp->dev; struct bnxt_vnic_info *vnic = &bp->vnic_info[BNXT_VNIC_DEFAULT]; struct netdev_hw_addr *ha; int i, off = 0, rc; - bool uc_update; - - netif_addr_lock_bh(dev); - uc_update = bnxt_uc_list_updated(bp); - netif_addr_unlock_bh(dev); if (!uc_update) goto skip_uc; @@ -13739,10 +13741,10 @@ static int bnxt_cfg_rx_mode(struct bnxt *bp) vnic->uc_filter_count = 1; netif_addr_lock_bh(dev); - if (netdev_uc_count(dev) > (BNXT_MAX_UC_ADDRS - 1)) { + if (netdev_hw_addr_list_count(uc) > (BNXT_MAX_UC_ADDRS - 1)) { vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS; } else { - netdev_for_each_uc_addr(ha, dev) { + netdev_hw_addr_list_for_each(ha, uc) { memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN); off += ETH_ALEN; vnic->uc_filter_count++; @@ -14708,6 +14710,7 @@ static void bnxt_ulp_restart(struct bnxt *bp) static void bnxt_sp_task(struct work_struct *work) { struct bnxt *bp = container_of(work, struct bnxt, sp_task); + struct net_device *dev = bp->dev; set_bit(BNXT_STATE_IN_SP_TASK, &bp->state); smp_mb__after_atomic(); @@ -14721,9 +14724,6 @@ static void bnxt_sp_task(struct work_struct *work) bnxt_reenable_sriov(bp); } - if (test_and_clear_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event)) - bnxt_cfg_rx_mode(bp); - if (test_and_clear_bit(BNXT_RX_NTP_FLTR_SP_EVENT, &bp->sp_event)) bnxt_cfg_ntp_filters(bp); if (test_and_clear_bit(BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT, &bp->sp_event)) @@ -14788,6 +14788,13 @@ static void bnxt_sp_task(struct work_struct *work) /* These functions below will clear BNXT_STATE_IN_SP_TASK. They * must be the last functions to be called before exiting. */ + if (test_and_clear_bit(BNXT_RX_MASK_SP_EVENT, &bp->sp_event)) { + bnxt_lock_sp(bp); + if (test_bit(BNXT_STATE_OPEN, &bp->state)) + bnxt_cfg_rx_mode(bp, &dev->uc, true); + bnxt_unlock_sp(bp); + } + if (test_and_clear_bit(BNXT_RESET_TASK_SP_EVENT, &bp->sp_event)) bnxt_reset(bp, false); @@ -15989,7 +15996,7 @@ static const struct net_device_ops bnxt_netdev_ops = { .ndo_start_xmit = bnxt_start_xmit, .ndo_stop = bnxt_close, .ndo_get_stats64 = bnxt_get_stats64, - .ndo_set_rx_mode = bnxt_set_rx_mode, + .ndo_set_rx_mode_async = bnxt_set_rx_mode, .ndo_eth_ioctl = bnxt_ioctl, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = bnxt_change_mac_addr, @@ -17356,9 +17363,14 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev) netdev_info(bp->dev, "PCI Slot Reset\n"); - if (!(bp->flags & BNXT_FLAG_CHIP_P5_PLUS) && - test_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state)) - msleep(900); + if (test_bit(BNXT_STATE_PCI_CHANNEL_IO_FROZEN, &bp->state)) { + /* After DPC, the chip should return CRS when the vendor ID + * config register is read until it is ready. On all chips, + * this is not happening reliably so add a 5-second delay as a + * workaround. + */ + msleep(5000); + } netdev_lock(netdev); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 53f336db4fcc..5d41dc1bc782 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -419,31 +419,13 @@ void bnxt_ptp_reapply_pps(struct bnxt *bp) } } -static int bnxt_get_target_cycles(struct bnxt_ptp_cfg *ptp, u64 target_ns, - u64 *cycles_delta) -{ - u64 cycles_now; - u64 nsec_now, nsec_delta; - int rc; - - rc = bnxt_refclk_read(ptp->bp, NULL, &cycles_now); - if (rc) - return rc; - - nsec_now = bnxt_timecounter_cyc2time(ptp, cycles_now); - - nsec_delta = target_ns - nsec_now; - *cycles_delta = div64_u64(nsec_delta << ptp->cc.shift, ptp->cc.mult); - return 0; -} - static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp, struct ptp_clock_request *rq) { struct hwrm_func_ptp_cfg_input *req; struct bnxt *bp = ptp->bp; struct timespec64 ts; - u64 target_ns, delta; + u64 target_ns; u16 enables; int rc; @@ -451,10 +433,6 @@ static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp, ts.tv_nsec = rq->perout.start.nsec; target_ns = timespec64_to_ns(&ts); - rc = bnxt_get_target_cycles(ptp, target_ns, &delta); - if (rc) - return rc; - rc = hwrm_req_init(bp, req, HWRM_FUNC_PTP_CFG); if (rc) return rc; @@ -468,7 +446,10 @@ static int bnxt_ptp_perout_cfg(struct bnxt_ptp_cfg *ptp, req->ptp_freq_adj_dll_phase = 0; req->ptp_freq_adj_ext_period = cpu_to_le32(NSEC_PER_SEC); req->ptp_freq_adj_ext_up = 0; - req->ptp_freq_adj_ext_phase_lower = cpu_to_le32(delta); + req->ptp_freq_adj_ext_phase_lower = + cpu_to_le32(lower_32_bits(target_ns)); + req->ptp_freq_adj_ext_phase_upper = + cpu_to_le32(upper_32_bits(target_ns)); return hwrm_req_send(bp, req); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index 052bf69cfa4c..5c751933da6a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -175,8 +175,14 @@ int bnxt_register_dev(struct bnxt_en_dev *edev, ulp->handle = handle; rcu_assign_pointer(ulp->ulp_ops, ulp_ops); - if (test_bit(BNXT_STATE_OPEN, &bp->state)) - bnxt_hwrm_vnic_cfg(bp, &bp->vnic_info[BNXT_VNIC_DEFAULT]); + if (test_bit(BNXT_STATE_OPEN, &bp->state)) { + rc = bnxt_hwrm_vnic_cfg(bp, &bp->vnic_info[BNXT_VNIC_DEFAULT]); + if (rc) { + netdev_err(dev, "Failed to configure dual VNIC mode\n"); + RCU_INIT_POINTER(ulp->ulp_ops, NULL); + goto exit; + } + } edev->ulp_tbl->msix_requested = bnxt_get_ulp_msix_num(bp); diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index fa5857923db4..b4bfd6c174e7 100644 --- a/drivers/net/ethernet/cirrus/cs89x0.c +++ b/drivers/net/ethernet/cirrus/cs89x0.c @@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = { static void __init reset_chip(struct net_device *dev) { -#if !defined(CONFIG_MACH_MX31ADS) struct net_local *lp = netdev_priv(dev); unsigned long reset_start_time; @@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev) while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && time_before(jiffies, reset_start_time + 2)) ; -#endif /* !CONFIG_MACH_MX31ADS */ } /* This is the real probe routine. diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 4824232f4890..4c762229ce42 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -122,6 +122,9 @@ struct gemini_ethernet_port { struct napi_struct napi; struct hrtimer rx_coalesce_timer; unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; + unsigned int rx_frag_nr; + unsigned int freeq_refill; struct gmac_txq txq[TX_QUEUE_NUM]; unsigned int txq_order; @@ -1442,10 +1445,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) unsigned short m = (1 << port->rxq_order) - 1; struct gemini_ethernet *geth = port->geth; void __iomem *ptr_reg = port->rxq_rwptr; + unsigned int frag_nr = port->rx_frag_nr; + struct sk_buff *skb = port->rx_skb; unsigned int frame_len, frag_len; struct gmac_rxdesc *rx = NULL; struct gmac_queue_page *gpage; - static struct sk_buff *skb; union gmac_rxdesc_0 word0; union gmac_rxdesc_1 word1; union gmac_rxdesc_3 word3; @@ -1455,7 +1459,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) unsigned short r, w; union dma_rwptr rw; dma_addr_t mapping; - int frag_nr = 0; spin_lock_irqsave(&geth->irq_lock, flags); rw.bits32 = readl(ptr_reg); @@ -1491,6 +1494,12 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); if (!gpage) { dev_err(geth->dev, "could not find mapping\n"); + port->stats.rx_dropped++; + if (skb) { + napi_free_frags(&port->napi); + skb = NULL; + frag_nr = 0; + } continue; } page = gpage->page; @@ -1499,6 +1508,8 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) if (skb) { napi_free_frags(&port->napi); port->stats.rx_dropped++; + skb = NULL; + frag_nr = 0; } skb = gmac_skb_if_good_frame(port, word0, frame_len); @@ -1533,6 +1544,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) if (word3.bits32 & EOF_BIT) { napi_gro_frags(&port->napi); skb = NULL; + frag_nr = 0; --budget; } continue; @@ -1541,6 +1553,7 @@ err_drop: if (skb) { napi_free_frags(&port->napi); skb = NULL; + frag_nr = 0; } if (mapping) @@ -1549,6 +1562,8 @@ err_drop: port->stats.rx_dropped++; } + port->rx_skb = skb; + port->rx_frag_nr = frag_nr; writew(r, ptr_reg); return budget; } @@ -1876,6 +1891,8 @@ static int gmac_stop(struct net_device *netdev) gmac_disable_tx_rx(netdev); gmac_stop_dma(port); napi_disable(&port->napi); + port->rx_skb = NULL; + port->rx_frag_nr = 0; gmac_enable_irq(netdev, 0); gmac_cleanup_rxq(netdev); diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h index e663bb5e614e..e691144e8756 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.h +++ b/drivers/net/ethernet/freescale/enetc/enetc.h @@ -330,6 +330,7 @@ struct enetc_si { struct workqueue_struct *workqueue; struct work_struct rx_mode_task; struct dentry *debugfs_root; + struct enetc_msg_swbd msg; /* Only valid for VSI */ }; #define ENETC_SI_ALIGN 32 diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c b/drivers/net/ethernet/freescale/enetc/enetc_vf.c index 6c4b374bcb0e..df8e95cc47d0 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c @@ -17,11 +17,36 @@ static void enetc_msg_vsi_write_msg(struct enetc_hw *hw, enetc_wr(hw, ENETC_VSIMSGSNDAR0, val); } +static void enetc_msg_dma_free(struct device *dev, struct enetc_msg_swbd *msg) +{ + if (msg->vaddr) { + dma_free_coherent(dev, msg->size, msg->vaddr, msg->dma); + msg->vaddr = NULL; + } +} + static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) { + struct device *dev = &si->pdev->dev; int timeout = 100; u32 vsimsgsr; + /* The VSI mailbox may be busy if last message was not yet processed + * by PSI. So need to check the mailbox status before sending. + */ + vsimsgsr = enetc_rd(&si->hw, ENETC_VSIMSGSR); + if (vsimsgsr & ENETC_VSIMSGSR_MB) { + /* It is safe to free the DMA buffer here, the caller does + * not access the DMA buffer if enetc_msg_vsi_send() fails. + */ + enetc_msg_dma_free(dev, msg); + dev_err(dev, "VSI mailbox is busy\n"); + return -EIO; + } + + /* Free the DMA buffer of the last message */ + enetc_msg_dma_free(dev, &si->msg); + si->msg = *msg; enetc_msg_vsi_write_msg(&si->hw, msg); do { @@ -32,12 +57,15 @@ static int enetc_msg_vsi_send(struct enetc_si *si, struct enetc_msg_swbd *msg) usleep_range(1000, 2000); } while (--timeout); - if (!timeout) + if (!timeout) { + dev_err(dev, "VSI mailbox timeout\n"); + return -ETIMEDOUT; + } /* check for message delivery error */ if (vsimsgsr & ENETC_VSIMSGSR_MS) { - dev_err(&si->pdev->dev, "VSI command execute error: %d\n", + dev_err(dev, "VSI command execute error: %d\n", ENETC_SIMSGSR_GET_MC(vsimsgsr)); return -EIO; } @@ -50,7 +78,6 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv, { struct enetc_msg_cmd_set_primary_mac *cmd; struct enetc_msg_swbd msg; - int err; msg.size = ALIGN(sizeof(struct enetc_msg_cmd_set_primary_mac), 64); msg.vaddr = dma_alloc_coherent(priv->dev, msg.size, &msg.dma, @@ -67,11 +94,7 @@ static int enetc_msg_vsi_set_primary_mac_addr(struct enetc_ndev_priv *priv, memcpy(&cmd->mac, saddr, sizeof(struct sockaddr)); /* send the command and wait */ - err = enetc_msg_vsi_send(priv->si, &msg); - - dma_free_coherent(priv->dev, msg.size, msg.vaddr, msg.dma); - - return err; + return enetc_msg_vsi_send(priv->si, &msg); } static int enetc_vf_set_mac_addr(struct net_device *ndev, void *addr) @@ -259,6 +282,7 @@ static void enetc_vf_remove(struct pci_dev *pdev) { struct enetc_si *si = pci_get_drvdata(pdev); struct enetc_ndev_priv *priv; + struct enetc_msg_swbd msg; priv = netdev_priv(si->ndev); unregister_netdev(si->ndev); @@ -270,7 +294,9 @@ static void enetc_vf_remove(struct pci_dev *pdev) free_netdev(si->ndev); + msg = si->msg; enetc_pci_remove(pdev); + enetc_msg_dma_free(&pdev->dev, &msg); } static const struct pci_device_id enetc_vf_id_table[] = { diff --git a/drivers/net/ethernet/freescale/enetc/ntmp.c b/drivers/net/ethernet/freescale/enetc/ntmp.c index 703752995e93..c94a928622fd 100644 --- a/drivers/net/ethernet/freescale/enetc/ntmp.c +++ b/drivers/net/ethernet/freescale/enetc/ntmp.c @@ -7,6 +7,7 @@ #include <linux/dma-mapping.h> #include <linux/fsl/netc_global.h> #include <linux/iopoll.h> +#include <linux/vmalloc.h> #include "ntmp_private.h" @@ -42,6 +43,12 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, if (!cbdr->addr_base) return -ENOMEM; + cbdr->swcbd = vcalloc(cbd_num, sizeof(struct netc_swcbd)); + if (!cbdr->swcbd) { + dma_free_coherent(dev, size, cbdr->addr_base, cbdr->dma_base); + return -ENOMEM; + } + cbdr->dma_size = size; cbdr->bd_num = cbd_num; cbdr->regs = *regs; @@ -52,10 +59,10 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, cbdr->addr_base_align = PTR_ALIGN(cbdr->addr_base, NTMP_BASE_ADDR_ALIGN); - spin_lock_init(&cbdr->ring_lock); + mutex_init(&cbdr->ring_lock); cbdr->next_to_use = netc_read(cbdr->regs.pir); - cbdr->next_to_clean = netc_read(cbdr->regs.cir); + cbdr->next_to_clean = netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX; /* Step 1: Configure the base address of the Control BD Ring */ netc_write(cbdr->regs.bar0, lower_32_bits(cbdr->dma_base_align)); @@ -71,10 +78,24 @@ int ntmp_init_cbdr(struct netc_cbdr *cbdr, struct device *dev, } EXPORT_SYMBOL_GPL(ntmp_init_cbdr); +static void ntmp_free_data_mem(struct device *dev, struct netc_swcbd *swcbd) +{ + if (unlikely(!swcbd->buf)) + return; + + dma_free_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN, + swcbd->buf, swcbd->dma); +} + void ntmp_free_cbdr(struct netc_cbdr *cbdr) { /* Disable the Control BD Ring */ netc_write(cbdr->regs.mr, 0); + + for (int i = 0; i < cbdr->bd_num; i++) + ntmp_free_data_mem(cbdr->dev, &cbdr->swcbd[i]); + + vfree(cbdr->swcbd); dma_free_coherent(cbdr->dev, cbdr->dma_size, cbdr->addr_base, cbdr->dma_base); memset(cbdr, 0, sizeof(*cbdr)); @@ -94,40 +115,59 @@ static union netc_cbd *ntmp_get_cbd(struct netc_cbdr *cbdr, int index) static void ntmp_clean_cbdr(struct netc_cbdr *cbdr) { - union netc_cbd *cbd; - int i; + int i = cbdr->next_to_clean; + + while ((netc_read(cbdr->regs.cir) & NETC_CBDRCIR_INDEX) != i) { + union netc_cbd *cbd = ntmp_get_cbd(cbdr, i); + struct netc_swcbd *swcbd = &cbdr->swcbd[i]; - i = cbdr->next_to_clean; - while (netc_read(cbdr->regs.cir) != i) { - cbd = ntmp_get_cbd(cbdr, i); + ntmp_free_data_mem(cbdr->dev, swcbd); + memset(swcbd, 0, sizeof(*swcbd)); memset(cbd, 0, sizeof(*cbd)); i = (i + 1) % cbdr->bd_num; } + dma_wmb(); cbdr->next_to_clean = i; } -static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd) +static void ntmp_select_and_lock_cbdr(struct ntmp_user *user, + struct netc_cbdr **cbdr) +{ + /* Currently only ENETC is supported, and it has only one command + * BD ring. + */ + *cbdr = &user->ring[0]; + + mutex_lock(&(*cbdr)->ring_lock); +} + +static void ntmp_unlock_cbdr(struct netc_cbdr *cbdr) +{ + mutex_unlock(&cbdr->ring_lock); +} + +static int netc_xmit_ntmp_cmd(struct netc_cbdr *cbdr, union netc_cbd *cbd, + struct netc_swcbd *swcbd) { union netc_cbd *cur_cbd; - struct netc_cbdr *cbdr; - int i, err; + int i, err, used_bds; u16 status; u32 val; - /* Currently only i.MX95 ENETC is supported, and it only has one - * command BD ring - */ - cbdr = &user->ring[0]; - - spin_lock_bh(&cbdr->ring_lock); - - if (unlikely(!ntmp_get_free_cbd_num(cbdr))) + used_bds = cbdr->bd_num - ntmp_get_free_cbd_num(cbdr); + if (unlikely(used_bds >= NETC_CBDR_CLEAN_WORK)) { ntmp_clean_cbdr(cbdr); + if (unlikely(!ntmp_get_free_cbd_num(cbdr))) { + ntmp_free_data_mem(cbdr->dev, swcbd); + return -EBUSY; + } + } i = cbdr->next_to_use; cur_cbd = ntmp_get_cbd(cbdr, i); *cur_cbd = *cbd; + cbdr->swcbd[i] = *swcbd; dma_wmb(); /* Update producer index of both software and hardware */ @@ -135,11 +175,17 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd) cbdr->next_to_use = i; netc_write(cbdr->regs.pir, i); - err = read_poll_timeout_atomic(netc_read, val, val == i, - NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT, - true, cbdr->regs.cir); + err = read_poll_timeout(netc_read, val, + (val & NETC_CBDRCIR_INDEX) == i, + NETC_CBDR_DELAY_US, NETC_CBDR_TIMEOUT, + true, cbdr->regs.cir); if (unlikely(err)) - goto cbdr_unlock; + return err; + + if (unlikely(val & NETC_CBDRCIR_SBE)) { + dev_err(cbdr->dev, "Command BD system bus error\n"); + return -EIO; + } dma_rmb(); /* Get the writeback command BD, because the caller may need @@ -150,40 +196,29 @@ static int netc_xmit_ntmp_cmd(struct ntmp_user *user, union netc_cbd *cbd) /* Check the writeback error status */ status = le16_to_cpu(cbd->resp_hdr.error_rr) & NTMP_RESP_ERROR; if (unlikely(status)) { - err = -EIO; - dev_err(user->dev, "Command BD error: 0x%04x\n", status); + dev_err(cbdr->dev, "Command BD error: 0x%04x\n", status); + return -EIO; } - ntmp_clean_cbdr(cbdr); - dma_wmb(); - -cbdr_unlock: - spin_unlock_bh(&cbdr->ring_lock); - - return err; + return 0; } -static int ntmp_alloc_data_mem(struct ntmp_dma_buf *data, void **buf_align) +static int ntmp_alloc_data_mem(struct device *dev, struct netc_swcbd *swcbd, + void **buf_align) { void *buf; - buf = dma_alloc_coherent(data->dev, data->size + NTMP_DATA_ADDR_ALIGN, - &data->dma, GFP_KERNEL); + buf = dma_alloc_coherent(dev, swcbd->size + NTMP_DATA_ADDR_ALIGN, + &swcbd->dma, GFP_KERNEL); if (!buf) return -ENOMEM; - data->buf = buf; + swcbd->buf = buf; *buf_align = PTR_ALIGN(buf, NTMP_DATA_ADDR_ALIGN); return 0; } -static void ntmp_free_data_mem(struct ntmp_dma_buf *data) -{ - dma_free_coherent(data->dev, data->size + NTMP_DATA_ADDR_ALIGN, - data->buf, data->dma); -} - static void ntmp_fill_request_hdr(union netc_cbd *cbd, dma_addr_t dma, int len, int table_id, int cmd, int access_method) @@ -234,37 +269,39 @@ static int ntmp_delete_entry_by_id(struct ntmp_user *user, int tbl_id, u8 tbl_ver, u32 entry_id, u32 req_len, u32 resp_len) { - struct ntmp_dma_buf data = { - .dev = user->dev, + struct netc_swcbd swcbd = { .size = max(req_len, resp_len), }; struct ntmp_req_by_eid *req; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err; - err = ntmp_alloc_data_mem(&data, (void **)&req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; ntmp_fill_crd_eid(req, tbl_ver, 0, 0, entry_id); - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(req_len, resp_len), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(req_len, resp_len), tbl_id, NTMP_CMD_DELETE, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) dev_err(user->dev, "Failed to delete entry 0x%x of %s, err: %pe", entry_id, ntmp_table_name(tbl_id), ERR_PTR(err)); - - ntmp_free_data_mem(&data); + ntmp_unlock_cbdr(cbdr); return err; } -static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, - u32 len, struct ntmp_req_by_eid *req, - dma_addr_t dma, bool compare_eid) +static int ntmp_query_entry_by_id(struct netc_cbdr *cbdr, int tbl_id, + struct ntmp_req_by_eid *req, + struct netc_swcbd *swcbd, + bool compare_eid) { + u32 len = NTMP_LEN(sizeof(*req), swcbd->size); struct ntmp_cmn_resp_query *resp; int cmd = NTMP_CMD_QUERY; union netc_cbd cbd; @@ -276,10 +313,11 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, cmd = NTMP_CMD_QU; /* Request header */ - ntmp_fill_request_hdr(&cbd, dma, len, tbl_id, cmd, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + ntmp_fill_request_hdr(&cbd, swcbd->dma, len, tbl_id, cmd, + NTMP_AM_ENTRY_ID); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, swcbd); if (err) { - dev_err(user->dev, + dev_err(cbdr->dev, "Failed to query entry 0x%x of %s, err: %pe\n", entry_id, ntmp_table_name(tbl_id), ERR_PTR(err)); return err; @@ -293,7 +331,7 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, resp = (struct ntmp_cmn_resp_query *)req; if (unlikely(le32_to_cpu(resp->entry_id) != entry_id)) { - dev_err(user->dev, + dev_err(cbdr->dev, "%s: query EID 0x%x doesn't match response EID 0x%x\n", ntmp_table_name(tbl_id), entry_id, le32_to_cpu(resp->entry_id)); return -EIO; @@ -305,15 +343,15 @@ static int ntmp_query_entry_by_id(struct ntmp_user *user, int tbl_id, int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id, struct maft_entry_data *maft) { - struct ntmp_dma_buf data = { - .dev = user->dev, + struct netc_swcbd swcbd = { .size = sizeof(struct maft_req_add), }; struct maft_req_add *req; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err; - err = ntmp_alloc_data_mem(&data, (void **)&req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; @@ -322,14 +360,15 @@ int ntmp_maft_add_entry(struct ntmp_user *user, u32 entry_id, req->keye = maft->keye; req->cfge = maft->cfge; - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(data.size, 0), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0), NTMP_MAFT_ID, NTMP_CMD_ADD, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) dev_err(user->dev, "Failed to add MAFT entry 0x%x, err: %pe\n", entry_id, ERR_PTR(err)); - - ntmp_free_data_mem(&data); + ntmp_unlock_cbdr(cbdr); return err; } @@ -338,31 +377,31 @@ EXPORT_SYMBOL_GPL(ntmp_maft_add_entry); int ntmp_maft_query_entry(struct ntmp_user *user, u32 entry_id, struct maft_entry_data *maft) { - struct ntmp_dma_buf data = { - .dev = user->dev, + struct netc_swcbd swcbd = { .size = sizeof(struct maft_resp_query), }; struct maft_resp_query *resp; struct ntmp_req_by_eid *req; + struct netc_cbdr *cbdr; int err; - err = ntmp_alloc_data_mem(&data, (void **)&req); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; ntmp_fill_crd_eid(req, user->tbl.maft_ver, 0, 0, entry_id); - err = ntmp_query_entry_by_id(user, NTMP_MAFT_ID, - NTMP_LEN(sizeof(*req), data.size), - req, data.dma, true); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = ntmp_query_entry_by_id(cbdr, NTMP_MAFT_ID, req, &swcbd, true); if (err) - goto end; + goto unlock_cbdr; resp = (struct maft_resp_query *)req; maft->keye = resp->keye; maft->cfge = resp->cfge; -end: - ntmp_free_data_mem(&data); +unlock_cbdr: + ntmp_unlock_cbdr(cbdr); return err; } @@ -378,8 +417,9 @@ EXPORT_SYMBOL_GPL(ntmp_maft_delete_entry); int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table, int count) { - struct ntmp_dma_buf data = {.dev = user->dev}; struct rsst_req_update *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err, i; @@ -387,8 +427,8 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table, /* HW only takes in a full 64 entry table */ return -EINVAL; - data.size = struct_size(req, groups, count); - err = ntmp_alloc_data_mem(&data, (void **)&req); + swcbd.size = struct_size(req, groups, count); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; @@ -398,15 +438,15 @@ int ntmp_rsst_update_entry(struct ntmp_user *user, const u32 *table, for (i = 0; i < count; i++) req->groups[i] = (u8)(table[i]); - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(data.size, 0), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(swcbd.size, 0), NTMP_RSST_ID, NTMP_CMD_UPDATE, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) dev_err(user->dev, "Failed to update RSST entry, err: %pe\n", ERR_PTR(err)); - - ntmp_free_data_mem(&data); + ntmp_unlock_cbdr(cbdr); return err; } @@ -414,8 +454,9 @@ EXPORT_SYMBOL_GPL(ntmp_rsst_update_entry); int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count) { - struct ntmp_dma_buf data = {.dev = user->dev}; struct ntmp_req_by_eid *req; + struct netc_swcbd swcbd; + struct netc_cbdr *cbdr; union netc_cbd cbd; int err, i; u8 *group; @@ -424,21 +465,23 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count) /* HW only takes in a full 64 entry table */ return -EINVAL; - data.size = NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count) + - RSST_CFGE_DATA_SIZE(count); - err = ntmp_alloc_data_mem(&data, (void **)&req); + swcbd.size = NTMP_ENTRY_ID_SIZE + RSST_STSE_DATA_SIZE(count) + + RSST_CFGE_DATA_SIZE(count); + err = ntmp_alloc_data_mem(user->dev, &swcbd, (void **)&req); if (err) return err; /* Set the request data buffer */ ntmp_fill_crd_eid(req, user->tbl.rsst_ver, 0, 0, 0); - ntmp_fill_request_hdr(&cbd, data.dma, NTMP_LEN(sizeof(*req), data.size), + ntmp_fill_request_hdr(&cbd, swcbd.dma, NTMP_LEN(sizeof(*req), swcbd.size), NTMP_RSST_ID, NTMP_CMD_QUERY, NTMP_AM_ENTRY_ID); - err = netc_xmit_ntmp_cmd(user, &cbd); + + ntmp_select_and_lock_cbdr(user, &cbdr); + err = netc_xmit_ntmp_cmd(cbdr, &cbd, &swcbd); if (err) { dev_err(user->dev, "Failed to query RSST entry, err: %pe\n", ERR_PTR(err)); - goto end; + goto unlock_cbdr; } group = (u8 *)req; @@ -446,8 +489,8 @@ int ntmp_rsst_query_entry(struct ntmp_user *user, u32 *table, int count) for (i = 0; i < count; i++) table[i] = group[i]; -end: - ntmp_free_data_mem(&data); +unlock_cbdr: + ntmp_unlock_cbdr(cbdr); return err; } diff --git a/drivers/net/ethernet/freescale/enetc/ntmp_private.h b/drivers/net/ethernet/freescale/enetc/ntmp_private.h index 34394e40fddd..f8dff3ba2c28 100644 --- a/drivers/net/ethernet/freescale/enetc/ntmp_private.h +++ b/drivers/net/ethernet/freescale/enetc/ntmp_private.h @@ -12,6 +12,9 @@ #define NTMP_EID_REQ_LEN 8 #define NETC_CBDR_BD_NUM 256 +#define NETC_CBDRCIR_INDEX GENMASK(9, 0) +#define NETC_CBDRCIR_SBE BIT(31) +#define NETC_CBDR_CLEAN_WORK 16 union netc_cbd { struct { @@ -54,13 +57,6 @@ union netc_cbd { } resp_hdr; /* NTMP Response Message Header Format */ }; -struct ntmp_dma_buf { - struct device *dev; - size_t size; - void *buf; - dma_addr_t dma; -}; - struct ntmp_cmn_req_data { __le16 update_act; u8 dbg_opt; diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig deleted file mode 100644 index 06a28bce5d27..000000000000 --- a/drivers/net/ethernet/fujitsu/Kconfig +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Fujitsu Network device configuration -# - -config NET_VENDOR_FUJITSU - bool "Fujitsu devices" - default y - depends on PCMCIA - help - If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - the questions about Fujitsu cards. If you say Y, you will be asked for - your specific card in the following questions. - -if NET_VENDOR_FUJITSU - -config PCMCIA_FMVJ18X - tristate "Fujitsu FMV-J18x PCMCIA support" - depends on PCMCIA && HAS_IOPORT - select CRC32 - help - Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible - PCMCIA (PC-card) Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called fmvj18x_cs. If unsure, say N. - -endif # NET_VENDOR_FUJITSU diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile deleted file mode 100644 index 74feebbf4572..000000000000 --- a/drivers/net/ethernet/fujitsu/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Fujitsu network device drivers. -# - -obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o diff --git a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c b/drivers/net/ethernet/fujitsu/fmvj18x_cs.c deleted file mode 100644 index 4859493471db..000000000000 --- a/drivers/net/ethernet/fujitsu/fmvj18x_cs.c +++ /dev/null @@ -1,1176 +0,0 @@ -/*====================================================================== - fmvj18x_cs.c 2.8 2002/03/23 - - A fmvj18x (and its compatibles) PCMCIA client driver - - Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp - - TDK LAK-CD021 and CONTEC C-NET(PC)C support added by - Nobuhiro Katayama, kata-n@po.iijnet.or.jp - - The PCMCIA client code is based on code written by David Hinds. - Network code is based on the "FMV-18x driver" by Yutaka TAMIYA - but is actually largely Donald Becker's AT1700 driver, which - carries the following attribution: - - Written 1993-94 by Donald Becker. - - Copyright 1993 United States Government as represented by the - Director, National Security Agency. - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - -======================================================================*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "fmvj18x_cs" -#define DRV_VERSION "2.9" - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/delay.h> -#include <linux/ethtool.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ioport.h> -#include <linux/crc32.h> - -#include <pcmcia/cistpl.h> -#include <pcmcia/ciscode.h> -#include <pcmcia/ds.h> - -#include <linux/uaccess.h> -#include <asm/io.h> - -/*====================================================================*/ - -/* Module parameters */ - -MODULE_DESCRIPTION("fmvj18x and compatible PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* SRAM configuration */ -/* 0:4KB*2 TX buffer else:8KB*2 TX buffer */ -INT_MODULE_PARM(sram_config, 0); - - -/*====================================================================*/ -/* - PCMCIA event handlers - */ -static int fmvj18x_config(struct pcmcia_device *link); -static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id); -static int fmvj18x_setup_mfc(struct pcmcia_device *link); -static void fmvj18x_release(struct pcmcia_device *link); -static void fmvj18x_detach(struct pcmcia_device *p_dev); - -/* - LAN controller(MBH86960A) specific routines - */ -static int fjn_config(struct net_device *dev, struct ifmap *map); -static int fjn_open(struct net_device *dev); -static int fjn_close(struct net_device *dev); -static netdev_tx_t fjn_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t fjn_interrupt(int irq, void *dev_id); -static void fjn_rx(struct net_device *dev); -static void fjn_reset(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue); -static const struct ethtool_ops netdev_ethtool_ops; - -/* - card type - */ -enum cardtype { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN, - XXX10304, NEC, KME -}; - -/* - driver specific data structure -*/ -struct local_info { - struct pcmcia_device *p_dev; - long open_time; - uint tx_started:1; - uint tx_queue; - u_short tx_queue_len; - enum cardtype cardtype; - u_short sent; - u_char __iomem *base; -}; - -#define MC_FILTERBREAK 64 - -/*====================================================================*/ -/* - ioport offset from the base address - */ -#define TX_STATUS 0 /* transmit status register */ -#define RX_STATUS 1 /* receive status register */ -#define TX_INTR 2 /* transmit interrupt mask register */ -#define RX_INTR 3 /* receive interrupt mask register */ -#define TX_MODE 4 /* transmit mode register */ -#define RX_MODE 5 /* receive mode register */ -#define CONFIG_0 6 /* configuration register 0 */ -#define CONFIG_1 7 /* configuration register 1 */ - -#define NODE_ID 8 /* node ID register (bank 0) */ -#define MAR_ADR 8 /* multicast address registers (bank 1) */ - -#define DATAPORT 8 /* buffer mem port registers (bank 2) */ -#define TX_START 10 /* transmit start register */ -#define COL_CTRL 11 /* 16 collision control register */ -#define BMPR12 12 /* reserved */ -#define BMPR13 13 /* reserved */ -#define RX_SKIP 14 /* skip received packet register */ - -#define LAN_CTRL 16 /* LAN card control register */ - -#define MAC_ID 0x1a /* hardware address */ -#define UNGERMANN_MAC_ID 0x18 /* UNGERMANN-BASS hardware address */ - -/* - control bits - */ -#define ENA_TMT_OK 0x80 -#define ENA_TMT_REC 0x20 -#define ENA_COL 0x04 -#define ENA_16_COL 0x02 -#define ENA_TBUS_ERR 0x01 - -#define ENA_PKT_RDY 0x80 -#define ENA_BUS_ERR 0x40 -#define ENA_LEN_ERR 0x08 -#define ENA_ALG_ERR 0x04 -#define ENA_CRC_ERR 0x02 -#define ENA_OVR_FLO 0x01 - -/* flags */ -#define F_TMT_RDY 0x80 /* can accept new packet */ -#define F_NET_BSY 0x40 /* carrier is detected */ -#define F_TMT_OK 0x20 /* send packet successfully */ -#define F_SRT_PKT 0x10 /* short packet error */ -#define F_COL_ERR 0x04 /* collision error */ -#define F_16_COL 0x02 /* 16 collision error */ -#define F_TBUS_ERR 0x01 /* bus read error */ - -#define F_PKT_RDY 0x80 /* packet(s) in buffer */ -#define F_BUS_ERR 0x40 /* bus read error */ -#define F_LEN_ERR 0x08 /* short packet */ -#define F_ALG_ERR 0x04 /* frame error */ -#define F_CRC_ERR 0x02 /* CRC error */ -#define F_OVR_FLO 0x01 /* overflow error */ - -#define F_BUF_EMP 0x40 /* receive buffer is empty */ - -#define F_SKP_PKT 0x05 /* drop packet in buffer */ - -/* default bitmaps */ -#define D_TX_INTR ( ENA_TMT_OK ) -#define D_RX_INTR ( ENA_PKT_RDY | ENA_LEN_ERR \ - | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO ) -#define TX_STAT_M ( F_TMT_RDY ) -#define RX_STAT_M ( F_PKT_RDY | F_LEN_ERR \ - | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO ) - -/* commands */ -#define D_TX_MODE 0x06 /* no tests, detect carrier */ -#define ID_MATCHED 0x02 /* (RX_MODE) */ -#define RECV_ALL 0x03 /* (RX_MODE) */ -#define CONFIG0_DFL 0x5a /* 16bit bus, 4K x 2 Tx queues */ -#define CONFIG0_DFL_1 0x5e /* 16bit bus, 8K x 2 Tx queues */ -#define CONFIG0_RST 0xda /* Data Link Controller off (CONFIG_0) */ -#define CONFIG0_RST_1 0xde /* Data Link Controller off (CONFIG_0) */ -#define BANK_0 0xa0 /* bank 0 (CONFIG_1) */ -#define BANK_1 0xa4 /* bank 1 (CONFIG_1) */ -#define BANK_2 0xa8 /* bank 2 (CONFIG_1) */ -#define CHIP_OFF 0x80 /* contrl chip power off (CONFIG_1) */ -#define DO_TX 0x80 /* do transmit packet */ -#define SEND_PKT 0x81 /* send a packet */ -#define AUTO_MODE 0x07 /* Auto skip packet on 16 col detected */ -#define MANU_MODE 0x03 /* Stop and skip packet on 16 col */ -#define TDK_AUTO_MODE 0x47 /* Auto skip packet on 16 col detected */ -#define TDK_MANU_MODE 0x43 /* Stop and skip packet on 16 col */ -#define INTR_OFF 0x0d /* LAN controller ignores interrupts */ -#define INTR_ON 0x1d /* LAN controller will catch interrupts */ - -#define TX_TIMEOUT ((400*HZ)/1000) - -#define BANK_0U 0x20 /* bank 0 (CONFIG_1) */ -#define BANK_1U 0x24 /* bank 1 (CONFIG_1) */ -#define BANK_2U 0x28 /* bank 2 (CONFIG_1) */ - -static const struct net_device_ops fjn_netdev_ops = { - .ndo_open = fjn_open, - .ndo_stop = fjn_close, - .ndo_start_xmit = fjn_start_xmit, - .ndo_tx_timeout = fjn_tx_timeout, - .ndo_set_config = fjn_config, - .ndo_set_rx_mode = set_rx_mode, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int fmvj18x_probe(struct pcmcia_device *link) -{ - struct local_info *lp; - struct net_device *dev; - - dev_dbg(&link->dev, "fmvj18x_attach()\n"); - - /* Make up a FMVJ18x specific data structure */ - dev = alloc_etherdev(sizeof(struct local_info)); - if (!dev) - return -ENOMEM; - lp = netdev_priv(dev); - link->priv = dev; - lp->p_dev = link; - lp->base = NULL; - - /* The io structure describes IO port mapping */ - link->resource[0]->end = 32; - link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - /* General socket configuration */ - link->config_flags |= CONF_ENABLE_IRQ; - - dev->netdev_ops = &fjn_netdev_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->ethtool_ops = &netdev_ethtool_ops; - - return fmvj18x_config(link); -} /* fmvj18x_attach */ - -/*====================================================================*/ - -static void fmvj18x_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "fmvj18x_detach\n"); - - unregister_netdev(dev); - - fmvj18x_release(link); - - free_netdev(dev); -} /* fmvj18x_detach */ - -/*====================================================================*/ - -static int mfc_try_io_port(struct pcmcia_device *link) -{ - int i, ret; - static const unsigned int serial_base[5] = - { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - - for (i = 0; i < 5; i++) { - link->resource[1]->start = serial_base[i]; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - if (link->resource[1]->start == 0) { - link->resource[1]->end = 0; - pr_notice("out of resource for serial\n"); - } - ret = pcmcia_request_io(link); - if (ret == 0) - return ret; - } - return ret; -} - -static int ungermann_try_io_port(struct pcmcia_device *link) -{ - int ret; - unsigned int ioaddr; - /* - Ungermann-Bass Access/CARD accepts 0x300,0x320,0x340,0x360 - 0x380,0x3c0 only for ioport. - */ - for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) { - link->resource[0]->start = ioaddr; - ret = pcmcia_request_io(link); - if (ret == 0) { - /* calculate ConfigIndex value */ - link->config_index = - ((link->resource[0]->start & 0x0f0) >> 3) | 0x22; - return ret; - } - } - return ret; /* RequestIO failed */ -} - -static int fmvj18x_ioprobe(struct pcmcia_device *p_dev, void *priv_data) -{ - return 0; /* strange, but that's what the code did already before... */ -} - -static int fmvj18x_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct local_info *lp = netdev_priv(dev); - int i, ret; - unsigned int ioaddr; - enum cardtype cardtype; - char *card_name = "unknown"; - u8 *buf; - size_t len; - u_char buggybuf[32]; - u8 addr[ETH_ALEN]; - - dev_dbg(&link->dev, "fmvj18x_config\n"); - - link->io_lines = 5; - - len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf); - kfree(buf); - - if (len) { - /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */ - ret = pcmcia_loop_config(link, fmvj18x_ioprobe, NULL); - if (ret != 0) - goto failed; - - switch (link->manf_id) { - case MANFID_TDK: - cardtype = TDK; - if (link->card_id == PRODID_TDK_GN3410 || - link->card_id == PRODID_TDK_NP9610 || - link->card_id == PRODID_TDK_MN3200) { - /* MultiFunction Card */ - link->config_base = 0x800; - link->config_index = 0x47; - link->resource[1]->end = 8; - } - break; - case MANFID_NEC: - cardtype = NEC; /* MultiFunction Card */ - link->config_base = 0x800; - link->config_index = 0x47; - link->resource[1]->end = 8; - break; - case MANFID_KME: - cardtype = KME; /* MultiFunction Card */ - link->config_base = 0x800; - link->config_index = 0x47; - link->resource[1]->end = 8; - break; - case MANFID_CONTEC: - cardtype = CONTEC; - break; - case MANFID_FUJITSU: - if (link->config_base == 0x0fe0) - cardtype = MBH10302; - else if (link->card_id == PRODID_FUJITSU_MBH10302) - /* RATOC REX-5588/9822/4886's PRODID are 0004(=MBH10302), - but these are MBH10304 based card. */ - cardtype = MBH10304; - else if (link->card_id == PRODID_FUJITSU_MBH10304) - cardtype = MBH10304; - else - cardtype = LA501; - break; - default: - cardtype = MBH10304; - } - } else { - /* old type card */ - switch (link->manf_id) { - case MANFID_FUJITSU: - if (link->card_id == PRODID_FUJITSU_MBH10304) { - cardtype = XXX10304; /* MBH10304 with buggy CIS */ - link->config_index = 0x20; - } else { - cardtype = MBH10302; /* NextCom NC5310, etc. */ - link->config_index = 1; - } - break; - case MANFID_UNGERMANN: - cardtype = UNGERMANN; - break; - default: - cardtype = MBH10302; - link->config_index = 1; - } - } - - if (link->resource[1]->end != 0) { - ret = mfc_try_io_port(link); - if (ret != 0) goto failed; - } else if (cardtype == UNGERMANN) { - ret = ungermann_try_io_port(link); - if (ret != 0) goto failed; - } else { - ret = pcmcia_request_io(link); - if (ret) - goto failed; - } - ret = pcmcia_request_irq(link, fjn_interrupt); - if (ret) - goto failed; - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - dev->irq = link->irq; - dev->base_addr = link->resource[0]->start; - - if (resource_size(link->resource[1]) != 0) { - ret = fmvj18x_setup_mfc(link); - if (ret != 0) goto failed; - } - - ioaddr = dev->base_addr; - - /* Reset controller */ - if (sram_config == 0) - outb(CONFIG0_RST, ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1, ioaddr + CONFIG_0); - - /* Power On chip and select bank 0 */ - if (cardtype == MBH10302) - outb(BANK_0, ioaddr + CONFIG_1); - else - outb(BANK_0U, ioaddr + CONFIG_1); - - /* Set hardware address */ - switch (cardtype) { - case MBH10304: - case TDK: - case LA501: - case CONTEC: - case NEC: - case KME: - if (cardtype == MBH10304) { - card_name = "FMV-J182"; - - len = pcmcia_get_tuple(link, CISTPL_FUNCE, &buf); - if (len < 11) { - kfree(buf); - goto failed; - } - /* Read MACID from CIS */ - eth_hw_addr_set(dev, &buf[5]); - kfree(buf); - } else { - if (pcmcia_get_mac_from_cis(link, dev)) - goto failed; - if( cardtype == TDK ) { - card_name = "TDK LAK-CD021"; - } else if( cardtype == LA501 ) { - card_name = "LA501"; - } else if( cardtype == NEC ) { - card_name = "PK-UG-J001"; - } else if( cardtype == KME ) { - card_name = "Panasonic"; - } else { - card_name = "C-NET(PC)C"; - } - } - break; - case UNGERMANN: - /* Read MACID from register */ - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + UNGERMANN_MAC_ID + i); - eth_hw_addr_set(dev, addr); - card_name = "Access/CARD"; - break; - case XXX10304: - /* Read MACID from Buggy CIS */ - if (fmvj18x_get_hwinfo(link, buggybuf) == -1) { - pr_notice("unable to read hardware net address\n"); - goto failed; - } - eth_hw_addr_set(dev, buggybuf); - card_name = "FMV-J182"; - break; - case MBH10302: - default: - /* Read MACID from register */ - for (i = 0; i < 6; i++) - addr[i] = inb(ioaddr + MAC_ID + i); - eth_hw_addr_set(dev, addr); - card_name = "FMV-J181"; - break; - } - - lp->cardtype = cardtype; - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - pr_notice("register_netdev() failed\n"); - goto failed; - } - - /* print current configuration */ - netdev_info(dev, "%s, sram %s, port %#3lx, irq %d, hw_addr %pM\n", - card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2", - dev->base_addr, dev->irq, dev->dev_addr); - - return 0; - -failed: - fmvj18x_release(link); - return -ENODEV; -} /* fmvj18x_config */ -/*====================================================================*/ - -static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) -{ - u_char __iomem *base; - int i, j; - - /* Allocate a small memory window */ - link->resource[2]->flags |= WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - link->resource[2]->start = 0; link->resource[2]->end = 0; - i = pcmcia_request_window(link, link->resource[2], 0); - if (i != 0) - return -1; - - base = ioremap(link->resource[2]->start, resource_size(link->resource[2])); - if (!base) { - pcmcia_release_window(link, link->resource[2]); - return -1; - } - - pcmcia_map_mem_page(link, link->resource[2], 0); - - /* - * MBH10304 CISTPL_FUNCE_LAN_NODE_ID format - * 22 0d xx xx xx 04 06 yy yy yy yy yy yy ff - * 'xx' is garbage. - * 'yy' is MAC address. - */ - for (i = 0; i < 0x200; i++) { - if (readb(base+i*2) == 0x22) { - if (readb(base+(i-1)*2) == 0xff && - readb(base+(i+5)*2) == 0x04 && - readb(base+(i+6)*2) == 0x06 && - readb(base+(i+13)*2) == 0xff) - break; - } - } - - if (i != 0x200) { - for (j = 0 ; j < 6; j++,i++) { - node_id[j] = readb(base+(i+7)*2); - } - } - - iounmap(base); - j = pcmcia_release_window(link, link->resource[2]); - return (i != 0x200) ? 0 : -1; - -} /* fmvj18x_get_hwinfo */ -/*====================================================================*/ - -static int fmvj18x_setup_mfc(struct pcmcia_device *link) -{ - int i; - struct net_device *dev = link->priv; - unsigned int ioaddr; - struct local_info *lp = netdev_priv(dev); - - /* Allocate a small memory window */ - link->resource[3]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - link->resource[3]->start = link->resource[3]->end = 0; - i = pcmcia_request_window(link, link->resource[3], 0); - if (i != 0) - return -1; - - lp->base = ioremap(link->resource[3]->start, - resource_size(link->resource[3])); - if (lp->base == NULL) { - netdev_notice(dev, "ioremap failed\n"); - return -1; - } - - i = pcmcia_map_mem_page(link, link->resource[3], 0); - if (i != 0) { - iounmap(lp->base); - lp->base = NULL; - return -1; - } - - ioaddr = dev->base_addr; - writeb(0x47, lp->base+0x800); /* Config Option Register of LAN */ - writeb(0x0, lp->base+0x802); /* Config and Status Register */ - - writeb(ioaddr & 0xff, lp->base+0x80a); /* I/O Base(Low) of LAN */ - writeb((ioaddr >> 8) & 0xff, lp->base+0x80c); /* I/O Base(High) of LAN */ - - writeb(0x45, lp->base+0x820); /* Config Option Register of Modem */ - writeb(0x8, lp->base+0x822); /* Config and Status Register */ - - return 0; - -} -/*====================================================================*/ - -static void fmvj18x_release(struct pcmcia_device *link) -{ - - struct net_device *dev = link->priv; - struct local_info *lp = netdev_priv(dev); - u_char __iomem *tmp; - - dev_dbg(&link->dev, "fmvj18x_release\n"); - - if (lp->base != NULL) { - tmp = lp->base; - lp->base = NULL; /* set NULL before iounmap */ - iounmap(tmp); - } - - pcmcia_disable_device(link); - -} - -static int fmvj18x_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int fmvj18x_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) { - fjn_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - -/*====================================================================*/ - -static const struct pcmcia_device_id fmvj18x_ids[] = { - PCMCIA_DEVICE_MANF_CARD(0x0004, 0x0004), - PCMCIA_DEVICE_PROD_ID12("EAGLE Technology", "NE200 ETHERNET LAN MBH10302 04", 0x528c88c4, 0x74f91e59), - PCMCIA_DEVICE_PROD_ID12("Eiger Labs,Inc", "EPX-10BT PC Card Ethernet 10BT", 0x53af556e, 0x877f9922), - PCMCIA_DEVICE_PROD_ID12("Eiger labs,Inc.", "EPX-10BT PC Card Ethernet 10BT", 0xf47e6c66, 0x877f9922), - PCMCIA_DEVICE_PROD_ID12("FUJITSU", "LAN Card(FMV-J182)", 0x6ee5a3d8, 0x5baf31db), - PCMCIA_DEVICE_PROD_ID12("FUJITSU", "MBH10308", 0x6ee5a3d8, 0x3f04875e), - PCMCIA_DEVICE_PROD_ID12("FUJITSU TOWA", "LA501", 0xb8451188, 0x12939ba2), - PCMCIA_DEVICE_PROD_ID12("HITACHI", "HT-4840-11", 0xf4f43949, 0x773910f4), - PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310B Ver1.0 ", 0x8cef4d3a, 0x075fc7b6), - PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310 Ver1.0 ", 0x8cef4d3a, 0xbccf43e6), - PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "10BASE_T CARD R280", 0x85c10e17, 0xd9413666), - PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CD02x", 0x1eae9475, 0x8fa0ee70), - PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CF010", 0x1eae9475, 0x7683bc9a), - PCMCIA_DEVICE_PROD_ID1("CONTEC Co.,Ltd.", 0x58d8fee2), - PCMCIA_DEVICE_PROD_ID1("PCMCIA LAN MBH10304 ES", 0x2599f454), - PCMCIA_DEVICE_PROD_ID1("PCMCIA MBH10302", 0x8f4005da), - PCMCIA_DEVICE_PROD_ID1("UBKK,V2.0", 0x90888080), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0d0a), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0e01), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0a05), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0b05), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x1101), - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, fmvj18x_ids); - -static struct pcmcia_driver fmvj18x_cs_driver = { - .owner = THIS_MODULE, - .name = "fmvj18x_cs", - .probe = fmvj18x_probe, - .remove = fmvj18x_detach, - .id_table = fmvj18x_ids, - .suspend = fmvj18x_suspend, - .resume = fmvj18x_resume, -}; -module_pcmcia_driver(fmvj18x_cs_driver); - -/*====================================================================*/ - -static irqreturn_t fjn_interrupt(int dummy, void *dev_id) -{ - struct net_device *dev = dev_id; - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr; - unsigned short tx_stat, rx_stat; - - ioaddr = dev->base_addr; - - /* avoid multiple interrupts */ - outw(0x0000, ioaddr + TX_INTR); - - /* wait for a while */ - udelay(1); - - /* get status */ - tx_stat = inb(ioaddr + TX_STATUS); - rx_stat = inb(ioaddr + RX_STATUS); - - /* clear status */ - outb(tx_stat, ioaddr + TX_STATUS); - outb(rx_stat, ioaddr + RX_STATUS); - - pr_debug("%s: interrupt, rx_status %02x.\n", dev->name, rx_stat); - pr_debug(" tx_status %02x.\n", tx_stat); - - if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { - /* there is packet(s) in rx buffer */ - fjn_rx(dev); - } - if (tx_stat & F_TMT_RDY) { - dev->stats.tx_packets += lp->sent ; - lp->sent = 0 ; - if (lp->tx_queue) { - outb(DO_TX | lp->tx_queue, ioaddr + TX_START); - lp->sent = lp->tx_queue ; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - netif_trans_update(dev); - } else { - lp->tx_started = 0; - } - netif_wake_queue(dev); - } - pr_debug("%s: exiting interrupt,\n", dev->name); - pr_debug(" tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat); - - outb(D_TX_INTR, ioaddr + TX_INTR); - outb(D_RX_INTR, ioaddr + RX_INTR); - - if (lp->base != NULL) { - /* Ack interrupt for multifunction card */ - writeb(0x01, lp->base+0x802); - writeb(0x09, lp->base+0x822); - } - - return IRQ_HANDLED; - -} /* fjn_interrupt */ - -/*====================================================================*/ - -static void fjn_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - netdev_notice(dev, "transmit timed out with status %04x, %s?\n", - htons(inw(ioaddr + TX_STATUS)), - inb(ioaddr + TX_STATUS) & F_TMT_RDY - ? "IRQ conflict" : "network cable problem"); - netdev_notice(dev, "timeout registers: %04x %04x %04x " - "%04x %04x %04x %04x %04x.\n", - htons(inw(ioaddr + 0)), htons(inw(ioaddr + 2)), - htons(inw(ioaddr + 4)), htons(inw(ioaddr + 6)), - htons(inw(ioaddr + 8)), htons(inw(ioaddr + 10)), - htons(inw(ioaddr + 12)), htons(inw(ioaddr + 14))); - dev->stats.tx_errors++; - /* ToDo: We should try to restart the adaptor... */ - local_irq_disable(); - fjn_reset(dev); - - lp->tx_started = 0; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - lp->sent = 0; - lp->open_time = jiffies; - local_irq_enable(); - netif_wake_queue(dev); -} - -static netdev_tx_t fjn_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - short length = skb->len; - - if (length < ETH_ZLEN) - { - if (skb_padto(skb, ETH_ZLEN)) - return NETDEV_TX_OK; - length = ETH_ZLEN; - } - - netif_stop_queue(dev); - - { - unsigned char *buf = skb->data; - - if (length > ETH_FRAME_LEN) { - netdev_notice(dev, "Attempting to send a large packet (%d bytes)\n", - length); - return NETDEV_TX_BUSY; - } - - netdev_dbg(dev, "Transmitting a packet of length %lu\n", - (unsigned long)skb->len); - dev->stats.tx_bytes += skb->len; - - /* Disable both interrupts. */ - outw(0x0000, ioaddr + TX_INTR); - - /* wait for a while */ - udelay(1); - - outw(length, ioaddr + DATAPORT); - outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1); - - lp->tx_queue++; - lp->tx_queue_len += ((length+3) & ~1); - - if (lp->tx_started == 0) { - /* If the Tx is idle, always trigger a transmit. */ - outb(DO_TX | lp->tx_queue, ioaddr + TX_START); - lp->sent = lp->tx_queue ; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - lp->tx_started = 1; - netif_start_queue(dev); - } else { - if( sram_config == 0 ) { - if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) ) - /* Yes, there is room for one more packet. */ - netif_start_queue(dev); - } else { - if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) && - lp->tx_queue < 127 ) - /* Yes, there is room for one more packet. */ - netif_start_queue(dev); - } - } - - /* Re-enable interrupts */ - outb(D_TX_INTR, ioaddr + TX_INTR); - outb(D_RX_INTR, ioaddr + RX_INTR); - } - dev_kfree_skb (skb); - - return NETDEV_TX_OK; -} /* fjn_start_xmit */ - -/*====================================================================*/ - -static void fjn_reset(struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - int i; - - netdev_dbg(dev, "fjn_reset() called\n"); - - /* Reset controller */ - if( sram_config == 0 ) - outb(CONFIG0_RST, ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1, ioaddr + CONFIG_0); - - /* Power On chip and select bank 0 */ - if (lp->cardtype == MBH10302) - outb(BANK_0, ioaddr + CONFIG_1); - else - outb(BANK_0U, ioaddr + CONFIG_1); - - /* Set Tx modes */ - outb(D_TX_MODE, ioaddr + TX_MODE); - /* set Rx modes */ - outb(ID_MATCHED, ioaddr + RX_MODE); - - /* Set hardware address */ - for (i = 0; i < 6; i++) - outb(dev->dev_addr[i], ioaddr + NODE_ID + i); - - /* (re)initialize the multicast table */ - set_rx_mode(dev); - - /* Switch to bank 2 (runtime mode) */ - if (lp->cardtype == MBH10302) - outb(BANK_2, ioaddr + CONFIG_1); - else - outb(BANK_2U, ioaddr + CONFIG_1); - - /* set 16col ctrl bits */ - if( lp->cardtype == TDK || lp->cardtype == CONTEC) - outb(TDK_AUTO_MODE, ioaddr + COL_CTRL); - else - outb(AUTO_MODE, ioaddr + COL_CTRL); - - /* clear Reserved Regs */ - outb(0x00, ioaddr + BMPR12); - outb(0x00, ioaddr + BMPR13); - - /* reset Skip packet reg. */ - outb(0x01, ioaddr + RX_SKIP); - - /* Enable Tx and Rx */ - if( sram_config == 0 ) - outb(CONFIG0_DFL, ioaddr + CONFIG_0); - else - outb(CONFIG0_DFL_1, ioaddr + CONFIG_0); - - /* Init receive pointer ? */ - inw(ioaddr + DATAPORT); - inw(ioaddr + DATAPORT); - - /* Clear all status */ - outb(0xff, ioaddr + TX_STATUS); - outb(0xff, ioaddr + RX_STATUS); - - if (lp->cardtype == MBH10302) - outb(INTR_OFF, ioaddr + LAN_CTRL); - - /* Turn on Rx interrupts */ - outb(D_TX_INTR, ioaddr + TX_INTR); - outb(D_RX_INTR, ioaddr + RX_INTR); - - /* Turn on interrupts from LAN card controller */ - if (lp->cardtype == MBH10302) - outb(INTR_ON, ioaddr + LAN_CTRL); -} /* fjn_reset */ - -/*====================================================================*/ - -static void fjn_rx(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int boguscount = 10; /* 5 -> 10: by agy 19940922 */ - - pr_debug("%s: in rx_packet(), rx_status %02x.\n", - dev->name, inb(ioaddr + RX_STATUS)); - - while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) { - u_short status = inw(ioaddr + DATAPORT); - - netdev_dbg(dev, "Rxing packet mode %02x status %04x.\n", - inb(ioaddr + RX_MODE), status); -#ifndef final_version - if (status == 0) { - outb(F_SKP_PKT, ioaddr + RX_SKIP); - break; - } -#endif - if ((status & 0xF0) != 0x20) { /* There was an error. */ - dev->stats.rx_errors++; - if (status & F_LEN_ERR) dev->stats.rx_length_errors++; - if (status & F_ALG_ERR) dev->stats.rx_frame_errors++; - if (status & F_CRC_ERR) dev->stats.rx_crc_errors++; - if (status & F_OVR_FLO) dev->stats.rx_over_errors++; - } else { - u_short pkt_len = inw(ioaddr + DATAPORT); - /* Malloc up new buffer. */ - struct sk_buff *skb; - - if (pkt_len > 1550) { - netdev_notice(dev, "The FMV-18x claimed a very large packet, size %d\n", - pkt_len); - outb(F_SKP_PKT, ioaddr + RX_SKIP); - dev->stats.rx_errors++; - break; - } - skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) { - outb(F_SKP_PKT, ioaddr + RX_SKIP); - dev->stats.rx_dropped++; - break; - } - - skb_reserve(skb, 2); - insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), - (pkt_len + 1) >> 1); - skb->protocol = eth_type_trans(skb, dev); - - { - int i; - pr_debug("%s: Rxed packet of length %d: ", - dev->name, pkt_len); - for (i = 0; i < 14; i++) - pr_debug(" %02x", skb->data[i]); - pr_debug(".\n"); - } - - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - if (--boguscount <= 0) - break; - } - - /* If any worth-while packets have been received, dev_rint() - has done a netif_wake_queue() for us and will work on them - when we get to the bottom-half routine. */ -/* - if (lp->cardtype != TDK) { - int i; - for (i = 0; i < 20; i++) { - if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP) - break; - (void)inw(ioaddr + DATAPORT); /+ dummy status read +/ - outb(F_SKP_PKT, ioaddr + RX_SKIP); - } - - if (i > 0) - pr_debug("%s: Exint Rx packet with mode %02x after " - "%d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i); - } -*/ -} /* fjn_rx */ - -/*====================================================================*/ - -static void netdev_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); - snprintf(info->bus_info, sizeof(info->bus_info), - "PCMCIA 0x%lx", dev->base_addr); -} - -static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, -}; - -static int fjn_config(struct net_device *dev, struct ifmap *map){ - return 0; -} - -static int fjn_open(struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - - pr_debug("fjn_open('%s').\n", dev->name); - - if (!pcmcia_dev_present(link)) - return -ENODEV; - - link->open++; - - fjn_reset(dev); - - lp->tx_started = 0; - lp->tx_queue = 0; - lp->tx_queue_len = 0; - lp->open_time = jiffies; - netif_start_queue(dev); - - return 0; -} /* fjn_open */ - -/*====================================================================*/ - -static int fjn_close(struct net_device *dev) -{ - struct local_info *lp = netdev_priv(dev); - struct pcmcia_device *link = lp->p_dev; - unsigned int ioaddr = dev->base_addr; - - pr_debug("fjn_close('%s').\n", dev->name); - - lp->open_time = 0; - netif_stop_queue(dev); - - /* Set configuration register 0 to disable Tx and Rx. */ - if( sram_config == 0 ) - outb(CONFIG0_RST ,ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0); - - /* Update the statistics -- ToDo. */ - - /* Power-down the chip. Green, green, green! */ - outb(CHIP_OFF ,ioaddr + CONFIG_1); - - /* Set the ethernet adaptor disable IRQ */ - if (lp->cardtype == MBH10302) - outb(INTR_OFF, ioaddr + LAN_CTRL); - - link->open--; - - return 0; -} /* fjn_close */ - -/*====================================================================*/ - -/* - Set the multicast/promiscuous mode for this adaptor. -*/ - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u_char mc_filter[8]; /* Multicast hash filter */ - u_long flags; - int i; - - int saved_bank; - int saved_config_0 = inb(ioaddr + CONFIG_0); - - local_irq_save(flags); - - /* Disable Tx and Rx */ - if (sram_config == 0) - outb(CONFIG0_RST, ioaddr + CONFIG_0); - else - outb(CONFIG0_RST_1, ioaddr + CONFIG_0); - - if (dev->flags & IFF_PROMISC) { - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */ - } else if (netdev_mc_count(dev) > MC_FILTERBREAK || - (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter perfectly -- accept all multicasts. */ - memset(mc_filter, 0xff, sizeof(mc_filter)); - outb(2, ioaddr + RX_MODE); /* Use normal mode. */ - } else if (netdev_mc_empty(dev)) { - memset(mc_filter, 0x00, sizeof(mc_filter)); - outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */ - } else { - struct netdev_hw_addr *ha; - - memset(mc_filter, 0, sizeof(mc_filter)); - netdev_for_each_mc_addr(ha, dev) { - unsigned int bit = ether_crc_le(ETH_ALEN, ha->addr) >> 26; - mc_filter[bit >> 3] |= (1 << (bit & 7)); - } - outb(2, ioaddr + RX_MODE); /* Use normal mode. */ - } - - /* Switch to bank 1 and set the multicast table. */ - saved_bank = inb(ioaddr + CONFIG_1); - outb(0xe4, ioaddr + CONFIG_1); - - for (i = 0; i < 8; i++) - outb(mc_filter[i], ioaddr + MAR_ADR + i); - outb(saved_bank, ioaddr + CONFIG_1); - - outb(saved_config_0, ioaddr + CONFIG_0); - - local_irq_restore(flags); -} diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 58cc3147afe2..73e051d26b9d 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1756,6 +1756,27 @@ static int ibmveth_set_mac_addr(struct net_device *dev, void *p) return 0; } +static netdev_features_t ibmveth_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + /* Some physical adapters do not support segmentation offload with + * MSS < 224. Disable GSO for such packets to avoid adapter freeze. + * Note: Single-segment packets (gso_segs == 1) don't need this check + * as they bypass the LSO path and are transmitted without segmentation. + */ + if (skb_is_gso(skb)) { + if (skb_shinfo(skb)->gso_size < IBMVETH_MIN_LSO_MSS) { + netdev_warn_once(dev, + "MSS %u too small for LSO, disabling GSO\n", + skb_shinfo(skb)->gso_size); + features &= ~NETIF_F_GSO_MASK; + } + } + + return vlan_features_check(skb, features); +} + static const struct net_device_ops ibmveth_netdev_ops = { .ndo_open = ibmveth_open, .ndo_stop = ibmveth_close, @@ -1767,6 +1788,7 @@ static const struct net_device_ops ibmveth_netdev_ops = { .ndo_set_features = ibmveth_set_features, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = ibmveth_set_mac_addr, + .ndo_features_check = ibmveth_features_check, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ibmveth_poll_controller, #endif diff --git a/drivers/net/ethernet/ibm/ibmveth.h b/drivers/net/ethernet/ibm/ibmveth.h index 068f99df133e..d87713668ed3 100644 --- a/drivers/net/ethernet/ibm/ibmveth.h +++ b/drivers/net/ethernet/ibm/ibmveth.h @@ -37,6 +37,7 @@ #define IBMVETH_ILLAN_IPV4_TCP_CSUM 0x0000000000000002UL #define IBMVETH_ILLAN_ACTIVE_TRUNK 0x0000000000000001UL +#define IBMVETH_MIN_LSO_MSS 224 /* Minimum MSS for LSO */ /* hcall macros */ #define h_register_logical_lan(ua, buflst, rxq, fltlst, mac) \ plpar_hcall_norets(H_REGISTER_LOGICAL_LAN, ua, buflst, rxq, fltlst, mac) diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 9befdacd6730..7ce0cc8ab8f4 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7706,6 +7706,7 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_register: if (!(adapter->flags & FLAG_HAS_AMT)) e1000e_release_hw_control(adapter); + e1000e_ptp_remove(adapter); err_eeprom: if (hw->phy.ops.check_reset_block && !hw->phy.ops.check_reset_block(hw)) e1000_phy_hw_reset(&adapter->hw); diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index dcb50c2e1aa2..83e780919ac9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -1318,6 +1318,7 @@ void i40e_ptp_restore_hw_time(struct i40e_pf *pf); void i40e_ptp_init(struct i40e_pf *pf); void i40e_ptp_stop(struct i40e_pf *pf); int i40e_ptp_alloc_pins(struct i40e_pf *pf); +void i40e_ptp_free_pins(struct i40e_pf *pf); int i40e_update_adq_vsi_queues(struct i40e_vsi *vsi, int vsi_offset); int i40e_is_vsi_uplink_mode_veb(struct i40e_vsi *vsi); int i40e_get_partition_bw_setting(struct i40e_pf *pf); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 926d001b2150..6d4f9218dc68 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -13783,7 +13783,6 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) netdev->neigh_priv_len = sizeof(u32) * 4; netdev->priv_flags |= IFF_UNICAST_FLT; - netdev->priv_flags |= IFF_SUPP_NOFCS; /* Setup netdev TC information */ i40e_vsi_config_netdev_tc(vsi, vsi->tc_config.enabled_tc); @@ -16109,9 +16108,11 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Unwind what we've done if something failed in the setup */ err_vsis: set_bit(__I40E_DOWN, pf->state); + i40e_ptp_stop(pf); i40e_clear_interrupt_scheme(pf); kfree(pf->vsi); err_switch_setup: + i40e_ptp_free_pins(pf); i40e_reset_interrupt_capability(pf); timer_shutdown_sync(&pf->service_timer); err_mac_addr: diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 404a716db8da..7d07c389bb23 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -940,12 +940,13 @@ int i40e_ptp_hwtstamp_get(struct net_device *netdev, * * Release memory allocated for PTP pins. **/ -static void i40e_ptp_free_pins(struct i40e_pf *pf) +void i40e_ptp_free_pins(struct i40e_pf *pf) { if (i40e_is_ptp_pin_dev(&pf->hw)) { kfree(pf->ptp_pins); kfree(pf->ptp_caps.pin_config); pf->ptp_pins = NULL; + pf->ptp_caps.pin_config = NULL; } } diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h index e9fb0a0919e3..050f8241ef5e 100644 --- a/drivers/net/ethernet/intel/iavf/iavf.h +++ b/drivers/net/ethernet/intel/iavf/iavf.h @@ -158,11 +158,10 @@ struct iavf_vlan { enum iavf_vlan_state_t { IAVF_VLAN_INVALID, IAVF_VLAN_ADD, /* filter needs to be added */ - IAVF_VLAN_IS_NEW, /* filter is new, wait for PF answer */ - IAVF_VLAN_ACTIVE, /* filter is accepted by PF */ - IAVF_VLAN_DISABLE, /* filter needs to be deleted by PF, then marked INACTIVE */ - IAVF_VLAN_INACTIVE, /* filter is inactive, we are in IFF_DOWN */ - IAVF_VLAN_REMOVE, /* filter needs to be removed from list */ + IAVF_VLAN_ADDING, /* ADD sent to PF, waiting for response */ + IAVF_VLAN_ACTIVE, /* PF confirmed, filter is in HW */ + IAVF_VLAN_REMOVE, /* filter queued for DEL from PF */ + IAVF_VLAN_REMOVING, /* DEL sent to PF, waiting for response */ }; struct iavf_vlan_filter { diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index dad001abc908..d2914c511e1e 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -757,10 +757,10 @@ iavf_vlan_filter *iavf_add_vlan(struct iavf_adapter *adapter, adapter->num_vlan_filters++; iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_VLAN_FILTER); } else if (f->state == IAVF_VLAN_REMOVE) { - /* Re-add the filter since we cannot tell whether the - * pending delete has already been processed by the PF. - * A duplicate add is harmless. - */ + /* DEL not yet sent to PF, cancel it */ + f->state = IAVF_VLAN_ACTIVE; + } else if (f->state == IAVF_VLAN_REMOVING) { + /* DEL already sent to PF, re-add after completion */ f->state = IAVF_VLAN_ADD; iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_VLAN_FILTER); @@ -791,37 +791,19 @@ static void iavf_del_vlan(struct iavf_adapter *adapter, struct iavf_vlan vlan) list_del(&f->list); kfree(f); adapter->num_vlan_filters--; - } else { + } else if (f->state != IAVF_VLAN_REMOVING) { f->state = IAVF_VLAN_REMOVE; iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_DEL_VLAN_FILTER); } + /* If REMOVING, DEL is already sent to PF; completion + * handler will free the filter when PF confirms. + */ } spin_unlock_bh(&adapter->mac_vlan_list_lock); } -/** - * iavf_restore_filters - * @adapter: board private structure - * - * Restore existing non MAC filters when VF netdev comes back up - **/ -static void iavf_restore_filters(struct iavf_adapter *adapter) -{ - struct iavf_vlan_filter *f; - - /* re-add all VLAN filters */ - spin_lock_bh(&adapter->mac_vlan_list_lock); - - list_for_each_entry(f, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_INACTIVE) - f->state = IAVF_VLAN_ADD; - } - - spin_unlock_bh(&adapter->mac_vlan_list_lock); - adapter->aq_required |= IAVF_FLAG_AQ_ADD_VLAN_FILTER; -} /** * iavf_get_num_vlans_added - get number of VLANs added @@ -1150,14 +1132,18 @@ bool iavf_promiscuous_mode_changed(struct iavf_adapter *adapter) /** * iavf_set_rx_mode - NDO callback to set the netdev filters * @netdev: network interface device structure + * @uc: snapshot of uc address list + * @mc: snapshot of mc address list **/ -static void iavf_set_rx_mode(struct net_device *netdev) +static void iavf_set_rx_mode(struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct iavf_adapter *adapter = netdev_priv(netdev); spin_lock_bh(&adapter->mac_vlan_list_lock); - __dev_uc_sync(netdev, iavf_addr_sync, iavf_addr_unsync); - __dev_mc_sync(netdev, iavf_addr_sync, iavf_addr_unsync); + __hw_addr_sync_dev(uc, netdev, iavf_addr_sync, iavf_addr_unsync); + __hw_addr_sync_dev(mc, netdev, iavf_addr_sync, iavf_addr_unsync); spin_unlock_bh(&adapter->mac_vlan_list_lock); spin_lock_bh(&adapter->current_netdev_promisc_flags_lock); @@ -1210,7 +1196,9 @@ static void iavf_configure(struct iavf_adapter *adapter) struct net_device *netdev = adapter->netdev; int i; - iavf_set_rx_mode(netdev); + netif_addr_lock_bh(netdev); + iavf_set_rx_mode(netdev, &netdev->uc, &netdev->mc); + netif_addr_unlock_bh(netdev); iavf_configure_tx(adapter); iavf_configure_rx(adapter); @@ -1240,13 +1228,12 @@ static void iavf_up_complete(struct iavf_adapter *adapter) } /** - * iavf_clear_mac_vlan_filters - Remove mac and vlan filters not sent to PF - * yet and mark other to be removed. + * iavf_clear_mac_filters - Remove MAC filters not sent to PF yet and mark + * others to be removed. * @adapter: board private structure **/ -static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter) +static void iavf_clear_mac_filters(struct iavf_adapter *adapter) { - struct iavf_vlan_filter *vlf, *vlftmp; struct iavf_mac_filter *f, *ftmp; spin_lock_bh(&adapter->mac_vlan_list_lock); @@ -1265,11 +1252,6 @@ static void iavf_clear_mac_vlan_filters(struct iavf_adapter *adapter) } } - /* disable all VLAN filters */ - list_for_each_entry_safe(vlf, vlftmp, &adapter->vlan_filter_list, - list) - vlf->state = IAVF_VLAN_DISABLE; - spin_unlock_bh(&adapter->mac_vlan_list_lock); } @@ -1365,7 +1347,7 @@ void iavf_down(struct iavf_adapter *adapter) iavf_napi_disable_all(adapter); iavf_irq_disable(adapter); - iavf_clear_mac_vlan_filters(adapter); + iavf_clear_mac_filters(adapter); iavf_clear_cloud_filters(adapter); iavf_clear_fdir_filters(adapter); iavf_clear_adv_rss_conf(adapter); @@ -1382,8 +1364,6 @@ void iavf_down(struct iavf_adapter *adapter) */ if (!list_empty(&adapter->mac_filter_list)) adapter->aq_required |= IAVF_FLAG_AQ_DEL_MAC_FILTER; - if (!list_empty(&adapter->vlan_filter_list)) - adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER; if (!list_empty(&adapter->cloud_filter_list)) adapter->aq_required |= IAVF_FLAG_AQ_DEL_CLOUD_FILTER; if (!list_empty(&adapter->fdir_list_head)) @@ -4488,8 +4468,6 @@ static int iavf_open(struct net_device *netdev) iavf_add_filter(adapter, adapter->hw.mac.addr); spin_unlock_bh(&adapter->mac_vlan_list_lock); - /* Restore filters that were removed with IFF_DOWN */ - iavf_restore_filters(adapter); iavf_restore_fdir_filters(adapter); iavf_configure(adapter); @@ -5153,7 +5131,7 @@ static const struct net_device_ops iavf_netdev_ops = { .ndo_open = iavf_open, .ndo_stop = iavf_close, .ndo_start_xmit = iavf_xmit_frame, - .ndo_set_rx_mode = iavf_set_rx_mode, + .ndo_set_rx_mode_async = iavf_set_rx_mode, .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = iavf_set_mac, .ndo_change_mtu = iavf_change_mtu, diff --git a/drivers/net/ethernet/intel/iavf/iavf_type.h b/drivers/net/ethernet/intel/iavf/iavf_type.h index 1d8cf29cb65a..5bb1de1cfd33 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_type.h +++ b/drivers/net/ethernet/intel/iavf/iavf_type.h @@ -277,7 +277,7 @@ struct iavf_rx_desc { /* L2 Tag 2 Presence */ #define IAVF_RXD_LEGACY_L2TAG2P_M BIT(0) /* Stripped S-TAG VLAN from the receive packet */ -#define IAVF_RXD_LEGACY_L2TAG2_M GENMASK_ULL(63, 32) +#define IAVF_RXD_LEGACY_L2TAG2_M GENMASK_ULL(63, 48) /* Stripped S-TAG VLAN from the receive packet */ #define IAVF_RXD_FLEX_L2TAG2_2_M GENMASK_ULL(63, 48) /* The packet is a UDP tunneled packet */ diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c index a52c100dcbc5..4f2defd2331b 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c +++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c @@ -746,7 +746,7 @@ static void iavf_vlan_add_reject(struct iavf_adapter *adapter) spin_lock_bh(&adapter->mac_vlan_list_lock); list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_IS_NEW) { + if (f->state == IAVF_VLAN_ADDING) { list_del(&f->list); kfree(f); adapter->num_vlan_filters--; @@ -812,7 +812,7 @@ void iavf_add_vlans(struct iavf_adapter *adapter) if (f->state == IAVF_VLAN_ADD) { vvfl->vlan_id[i] = f->vlan.vid; i++; - f->state = IAVF_VLAN_IS_NEW; + f->state = IAVF_VLAN_ADDING; if (i == count) break; } @@ -874,7 +874,7 @@ void iavf_add_vlans(struct iavf_adapter *adapter) vlan->tpid = f->vlan.tpid; i++; - f->state = IAVF_VLAN_IS_NEW; + f->state = IAVF_VLAN_ADDING; } } @@ -911,22 +911,12 @@ void iavf_del_vlans(struct iavf_adapter *adapter) spin_lock_bh(&adapter->mac_vlan_list_lock); list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - /* since VLAN capabilities are not allowed, we dont want to send - * a VLAN delete request because it will most likely fail and - * create unnecessary errors/noise, so just free the VLAN - * filters marked for removal to enable bailing out before - * sending a virtchnl message - */ if (f->state == IAVF_VLAN_REMOVE && !VLAN_FILTERING_ALLOWED(adapter)) { list_del(&f->list); kfree(f); adapter->num_vlan_filters--; - } else if (f->state == IAVF_VLAN_DISABLE && - !VLAN_FILTERING_ALLOWED(adapter)) { - f->state = IAVF_VLAN_INACTIVE; - } else if (f->state == IAVF_VLAN_REMOVE || - f->state == IAVF_VLAN_DISABLE) { + } else if (f->state == IAVF_VLAN_REMOVE) { count++; } } @@ -958,18 +948,10 @@ void iavf_del_vlans(struct iavf_adapter *adapter) vvfl->vsi_id = adapter->vsi_res->vsi_id; vvfl->num_elements = count; - list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_DISABLE) { - vvfl->vlan_id[i] = f->vlan.vid; - f->state = IAVF_VLAN_INACTIVE; - i++; - if (i == count) - break; - } else if (f->state == IAVF_VLAN_REMOVE) { + list_for_each_entry(f, &adapter->vlan_filter_list, list) { + if (f->state == IAVF_VLAN_REMOVE) { vvfl->vlan_id[i] = f->vlan.vid; - list_del(&f->list); - kfree(f); - adapter->num_vlan_filters--; + f->state = IAVF_VLAN_REMOVING; i++; if (i == count) break; @@ -1006,9 +988,8 @@ void iavf_del_vlans(struct iavf_adapter *adapter) vvfl_v2->vport_id = adapter->vsi_res->vsi_id; vvfl_v2->num_elements = count; - list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_DISABLE || - f->state == IAVF_VLAN_REMOVE) { + list_for_each_entry(f, &adapter->vlan_filter_list, list) { + if (f->state == IAVF_VLAN_REMOVE) { struct virtchnl_vlan_supported_caps *filtering_support = &adapter->vlan_v2_caps.filtering.filtering_support; struct virtchnl_vlan *vlan; @@ -1022,13 +1003,7 @@ void iavf_del_vlans(struct iavf_adapter *adapter) vlan->tci = f->vlan.vid; vlan->tpid = f->vlan.tpid; - if (f->state == IAVF_VLAN_DISABLE) { - f->state = IAVF_VLAN_INACTIVE; - } else { - list_del(&f->list); - kfree(f); - adapter->num_vlan_filters--; - } + f->state = IAVF_VLAN_REMOVING; i++; if (i == count) break; @@ -2391,10 +2366,6 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter, ether_addr_copy(adapter->hw.mac.addr, netdev->dev_addr); wake_up(&adapter->vc_waitqueue); break; - case VIRTCHNL_OP_DEL_VLAN: - dev_err(&adapter->pdev->dev, "Failed to delete VLAN filter, error %s\n", - iavf_stat_str(&adapter->hw, v_retval)); - break; case VIRTCHNL_OP_DEL_ETH_ADDR: dev_err(&adapter->pdev->dev, "Failed to delete MAC filter, error %s\n", iavf_stat_str(&adapter->hw, v_retval)); @@ -2905,17 +2876,42 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter, spin_unlock_bh(&adapter->adv_rss_lock); } break; + case VIRTCHNL_OP_ADD_VLAN: case VIRTCHNL_OP_ADD_VLAN_V2: { struct iavf_vlan_filter *f; + if (v_retval) + break; + spin_lock_bh(&adapter->mac_vlan_list_lock); list_for_each_entry(f, &adapter->vlan_filter_list, list) { - if (f->state == IAVF_VLAN_IS_NEW) + if (f->state == IAVF_VLAN_ADDING) f->state = IAVF_VLAN_ACTIVE; } spin_unlock_bh(&adapter->mac_vlan_list_lock); } break; + case VIRTCHNL_OP_DEL_VLAN: + case VIRTCHNL_OP_DEL_VLAN_V2: { + struct iavf_vlan_filter *f, *ftmp; + + spin_lock_bh(&adapter->mac_vlan_list_lock); + list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, + list) { + if (f->state == IAVF_VLAN_REMOVING) { + if (v_retval) { + /* PF rejected DEL, keep filter */ + f->state = IAVF_VLAN_ACTIVE; + } else { + list_del(&f->list); + kfree(f); + adapter->num_vlan_filters--; + } + } + } + spin_unlock_bh(&adapter->mac_vlan_list_lock); + } + break; case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: /* PF enabled vlan strip on this VF. * Update netdev->features if needed to be in sync with ethtool. diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink.c b/drivers/net/ethernet/intel/ice/devlink/devlink.c index 6144cee8034d..641d6e289d5c 100644 --- a/drivers/net/ethernet/intel/ice/devlink/devlink.c +++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c @@ -1245,6 +1245,8 @@ static int ice_devlink_reinit_up(struct ice_pf *pf) return err; } + ice_init_dev_hw(pf); + /* load MSI-X values */ ice_set_min_max_msix(pf); diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h index eb3a48330cc1..725b130dd3a2 100644 --- a/drivers/net/ethernet/intel/ice/ice.h +++ b/drivers/net/ethernet/intel/ice/ice.h @@ -753,7 +753,7 @@ static inline bool ice_is_xdp_ena_vsi(struct ice_vsi *vsi) static inline void ice_set_ring_xdp(struct ice_tx_ring *ring) { - ring->flags |= ICE_TX_FLAGS_RING_XDP; + set_bit(ICE_TX_RING_FLAGS_XDP, ring->flags); } /** @@ -778,7 +778,7 @@ static inline bool ice_is_txtime_ena(const struct ice_tx_ring *ring) */ static inline bool ice_is_txtime_cfg(const struct ice_tx_ring *ring) { - return !!(ring->flags & ICE_TX_FLAGS_TXTIME); + return test_bit(ICE_TX_RING_FLAGS_TXTIME, ring->flags); } /** diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h index 859e9c66f3e7..3cbb1b0582e3 100644 --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h @@ -1252,7 +1252,7 @@ struct ice_aqc_get_link_status_data { #define ICE_AQ_LINK_PWR_QSFP_CLASS_3 2 #define ICE_AQ_LINK_PWR_QSFP_CLASS_4 3 __le16 link_speed; -#define ICE_AQ_LINK_SPEED_M 0x7FF +#define ICE_AQ_LINK_SPEED_M GENMASK(11, 0) #define ICE_AQ_LINK_SPEED_10MB BIT(0) #define ICE_AQ_LINK_SPEED_100MB BIT(1) #define ICE_AQ_LINK_SPEED_1000MB BIT(2) diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c index ce11fea122d0..b617a6bff891 100644 --- a/drivers/net/ethernet/intel/ice/ice_common.c +++ b/drivers/net/ethernet/intel/ice/ice_common.c @@ -1126,8 +1126,6 @@ int ice_init_hw(struct ice_hw *hw) if (status) goto err_unroll_fltr_mgmt_struct; - ice_init_dev_hw(hw->back); - mutex_init(&hw->tnl_lock); ice_init_chk_recipe_reuse_support(hw); diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c index bd77f1c001ee..0bc6dd375687 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c @@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) struct ice_dcbx_cfg *err_cfg; int ret; + mutex_lock(&pf->tc_mutex); + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); if (ret) { dev_err(dev, "Query Port ETS failed\n"); goto dcb_error; } - mutex_lock(&pf->tc_mutex); - if (!pf->hw.port_info->qos_cfg.is_sw_lldp) ice_cfg_etsrec_defaults(pf->hw.port_info); @@ -943,7 +943,7 @@ ice_tx_prepare_vlan_flags_dcb(struct ice_tx_ring *tx_ring, /* if this is not already set it means a VLAN 0 + priority needs * to be offloaded */ - if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2) + if (test_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, tx_ring->flags)) first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN; else first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.c b/drivers/net/ethernet/intel/ice/ice_dpll.c index 62f75701d652..892bc7c2e28b 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.c +++ b/drivers/net/ethernet/intel/ice/ice_dpll.c @@ -1155,6 +1155,32 @@ ice_dpll_input_state_get(const struct dpll_pin *pin, void *pin_priv, } /** + * ice_dpll_sw_pin_notify_peer - notify the paired SW pin after a state change + * @d: pointer to dplls struct + * @changed: the SW pin that was explicitly changed (already notified by dpll core) + * + * SMA and U.FL pins share physical signal paths in pairs (SMA1/U.FL1 and + * SMA2/U.FL2). When one pin's routing changes via the PCA9575 GPIO + * expander, the paired pin's state may also change. Send a change + * notification for the peer pin so userspace consumers monitoring the + * peer via dpll netlink learn about the update. + * + * Context: Called from dpll_pin_ops callbacks after pf->dplls.lock is + * released. Uses __dpll_pin_change_ntf() because dpll_lock is + * still held by the dpll netlink layer. + */ +static void ice_dpll_sw_pin_notify_peer(struct ice_dplls *d, + struct ice_dpll_pin *changed) +{ + struct ice_dpll_pin *peer; + + peer = (changed >= d->sma && changed < d->sma + ICE_DPLL_PIN_SW_NUM) ? + &d->ufl[changed->idx] : &d->sma[changed->idx]; + if (peer->pin) + __dpll_pin_change_ntf(peer->pin); +} + +/** * ice_dpll_sma_direction_set - set direction of SMA pin * @p: pointer to a pin * @direction: requested direction of the pin @@ -1171,6 +1197,8 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, enum dpll_pin_direction direction, struct netlink_ext_ack *extack) { + struct ice_dplls *d = &p->pf->dplls; + struct ice_dpll_pin *peer; u8 data; int ret; @@ -1189,8 +1217,9 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, case ICE_DPLL_PIN_SW_2_IDX: if (direction == DPLL_PIN_DIRECTION_INPUT) { data &= ~ICE_SMA2_DIR_EN; + data |= ICE_SMA2_UFL2_RX_DIS; } else { - data &= ~ICE_SMA2_TX_EN; + data &= ~(ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS); data |= ICE_SMA2_DIR_EN; } break; @@ -1202,6 +1231,34 @@ static int ice_dpll_sma_direction_set(struct ice_dpll_pin *p, ret = ice_dpll_pin_state_update(p->pf, p, ICE_DPLL_PIN_TYPE_SOFTWARE, extack); + if (ret) + return ret; + + /* When a direction change activates the paired U.FL pin, enable + * its backing CGU pin so the pin reports as connected. Without + * this the U.FL routing is correct but the CGU pin stays disabled + * and userspace sees the pin as disconnected. Do not disable the + * backing pin when U.FL becomes inactive because the SMA pin may + * still be using it. + */ + peer = &d->ufl[p->idx]; + if (peer->active) { + struct ice_dpll_pin *target; + enum ice_dpll_pin_type type; + + if (peer->output) { + target = peer->output; + type = ICE_DPLL_PIN_TYPE_OUTPUT; + } else { + target = peer->input; + type = ICE_DPLL_PIN_TYPE_INPUT; + } + ret = ice_dpll_pin_enable(&p->pf->hw, target, + d->eec.dpll_idx, type, extack); + if (!ret) + ret = ice_dpll_pin_state_update(p->pf, target, + type, extack); + } return ret; } @@ -1253,6 +1310,14 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, data &= ~ICE_SMA1_MASK; enable = true; } else if (state == DPLL_PIN_STATE_DISCONNECTED) { + /* Skip if U.FL1 is not active, setting TX_EN + * while DIR_EN is set would also deactivate + * the paired SMA1 output. + */ + if (data & (ICE_SMA1_DIR_EN | ICE_SMA1_TX_EN)) { + ret = 0; + goto unlock; + } data |= ICE_SMA1_TX_EN; enable = false; } else { @@ -1267,6 +1332,15 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, data &= ~ICE_SMA2_UFL2_RX_DIS; enable = true; } else if (state == DPLL_PIN_STATE_DISCONNECTED) { + /* Skip if U.FL2 is not active, setting + * UFL2_RX_DIS could also disable the paired + * SMA2 input. + */ + if (!(data & ICE_SMA2_DIR_EN) || + (data & ICE_SMA2_UFL2_RX_DIS)) { + ret = 0; + goto unlock; + } data |= ICE_SMA2_UFL2_RX_DIS; enable = false; } else { @@ -1296,6 +1370,8 @@ ice_dpll_ufl_pin_state_set(const struct dpll_pin *pin, void *pin_priv, unlock: mutex_unlock(&pf->dplls.lock); + if (!ret) + ice_dpll_sw_pin_notify_peer(&pf->dplls, p); return ret; } @@ -1414,6 +1490,8 @@ ice_dpll_sma_pin_state_set(const struct dpll_pin *pin, void *pin_priv, unlock: mutex_unlock(&pf->dplls.lock); + if (!ret) + ice_dpll_sw_pin_notify_peer(&pf->dplls, sma); return ret; } @@ -1609,6 +1687,8 @@ ice_dpll_pin_sma_direction_set(const struct dpll_pin *pin, void *pin_priv, mutex_lock(&pf->dplls.lock); ret = ice_dpll_sma_direction_set(p, direction, extack); mutex_unlock(&pf->dplls.lock); + if (!ret) + ice_dpll_sw_pin_notify_peer(&pf->dplls, p); return ret; } @@ -1915,7 +1995,10 @@ ice_dpll_phase_offset_get(const struct dpll_pin *pin, void *pin_priv, d->active_input == p->input->pin)) *phase_offset = d->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR; else if (d->phase_offset_monitor_period) - *phase_offset = p->phase_offset * ICE_DPLL_PHASE_OFFSET_FACTOR; + *phase_offset = (p->input && + p->direction == DPLL_PIN_DIRECTION_INPUT ? + p->input->phase_offset : + p->phase_offset) * ICE_DPLL_PHASE_OFFSET_FACTOR; else *phase_offset = 0; mutex_unlock(&pf->dplls.lock); @@ -2440,6 +2523,8 @@ ice_dpll_rclk_state_on_pin_set(const struct dpll_pin *pin, void *pin_priv, if (hw_idx < 0) goto unlock; hw_idx -= pf->dplls.base_rclk_idx; + if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX) + goto unlock; if ((enable && p->state[hw_idx] == DPLL_PIN_STATE_CONNECTED) || (!enable && p->state[hw_idx] == DPLL_PIN_STATE_DISCONNECTED)) { @@ -2503,6 +2588,9 @@ ice_dpll_rclk_state_on_pin_get(const struct dpll_pin *pin, void *pin_priv, hw_idx = ice_dpll_pin_get_parent_idx(p, parent_pin); if (hw_idx < 0) goto unlock; + hw_idx -= pf->dplls.base_rclk_idx; + if (hw_idx >= ICE_DPLL_RCLK_NUM_MAX) + goto unlock; ret = ice_dpll_pin_state_update(pf, p, ICE_DPLL_PIN_TYPE_RCLK_INPUT, extack); @@ -2610,6 +2698,27 @@ static u64 ice_generate_clock_id(struct ice_pf *pf) } /** + * ice_dpll_pin_ntf - notify pin change including any SW pin wrappers + * @dplls: pointer to dplls struct + * @pin: the dpll_pin that changed + * + * Send a change notification for @pin and for any registered SMA/U.FL pin + * whose backing CGU input matches @pin. + */ +static void ice_dpll_pin_ntf(struct ice_dplls *dplls, struct dpll_pin *pin) +{ + dpll_pin_change_ntf(pin); + for (int i = 0; i < ICE_DPLL_PIN_SW_NUM; i++) { + if (dplls->sma[i].pin && dplls->sma[i].input && + dplls->sma[i].input->pin == pin) + dpll_pin_change_ntf(dplls->sma[i].pin); + if (dplls->ufl[i].pin && dplls->ufl[i].input && + dplls->ufl[i].input->pin == pin) + dpll_pin_change_ntf(dplls->ufl[i].pin); + } +} + +/** * ice_dpll_notify_changes - notify dpll subsystem about changes * @d: pointer do dpll * @@ -2617,6 +2726,7 @@ static u64 ice_generate_clock_id(struct ice_pf *pf) */ static void ice_dpll_notify_changes(struct ice_dpll *d) { + struct ice_dplls *dplls = &d->pf->dplls; bool pin_notified = false; if (d->prev_dpll_state != d->dpll_state) { @@ -2625,17 +2735,17 @@ static void ice_dpll_notify_changes(struct ice_dpll *d) } if (d->prev_input != d->active_input) { if (d->prev_input) - dpll_pin_change_ntf(d->prev_input); + ice_dpll_pin_ntf(dplls, d->prev_input); d->prev_input = d->active_input; if (d->active_input) { - dpll_pin_change_ntf(d->active_input); + ice_dpll_pin_ntf(dplls, d->active_input); pin_notified = true; } } if (d->prev_phase_offset != d->phase_offset) { d->prev_phase_offset = d->phase_offset; if (!pin_notified && d->active_input) - dpll_pin_change_ntf(d->active_input); + ice_dpll_pin_ntf(dplls, d->active_input); } } @@ -2664,6 +2774,7 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf) /** * ice_dpll_pins_notify_mask - notify dpll subsystem about bulk pin changes + * @dplls: pointer to dplls struct * @pins: array of ice_dpll_pin pointers registered within dpll subsystem * @pin_num: number of pins * @phase_offset_ntf_mask: bitmask of pin indexes to notify @@ -2673,15 +2784,14 @@ static bool ice_dpll_is_pps_phase_monitor(struct ice_pf *pf) * * Context: Must be called while pf->dplls.lock is released. */ -static void ice_dpll_pins_notify_mask(struct ice_dpll_pin *pins, +static void ice_dpll_pins_notify_mask(struct ice_dplls *dplls, + struct ice_dpll_pin *pins, u8 pin_num, u32 phase_offset_ntf_mask) { - int i = 0; - - for (i = 0; i < pin_num; i++) - if (phase_offset_ntf_mask & (1 << i)) - dpll_pin_change_ntf(pins[i].pin); + for (int i = 0; i < pin_num; i++) + if (phase_offset_ntf_mask & BIT(i)) + ice_dpll_pin_ntf(dplls, pins[i].pin); } /** @@ -2857,7 +2967,7 @@ static void ice_dpll_periodic_work(struct kthread_work *work) ice_dpll_notify_changes(de); ice_dpll_notify_changes(dp); if (phase_offset_ntf) - ice_dpll_pins_notify_mask(d->inputs, d->num_inputs, + ice_dpll_pins_notify_mask(d, d->inputs, d->num_inputs, phase_offset_ntf); resched: @@ -4014,6 +4124,7 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf) struct ice_dpll_pin *pin; u32 phase_adj_max, caps; int i, ret; + u8 data; if (pf->hw.device_id == ICE_DEV_ID_E810C_QSFP) input_idx_offset = ICE_E810_RCLK_PINS_NUM; @@ -4073,6 +4184,22 @@ static int ice_dpll_init_info_sw_pins(struct ice_pf *pf) } ice_dpll_phase_range_set(&pin->prop.phase_range, phase_adj_max); } + + /* Initialize the SMA control register to a known-good default state. + * Without this write the PCA9575 GPIO expander retains its power-on + * default (all outputs high) which makes all SW pins appear inactive. + * Set SMA1 and SMA2 as active inputs, disable U.FL1 output and + * U.FL2 input. + */ + ret = ice_read_sma_ctrl(&pf->hw, &data); + if (ret) + return ret; + data &= ~ICE_ALL_SMA_MASK; + data |= ICE_SMA1_TX_EN | ICE_SMA2_TX_EN | ICE_SMA2_UFL2_RX_DIS; + ret = ice_write_sma_ctrl(&pf->hw, data); + if (ret) + return ret; + ret = ice_dpll_pin_state_update(pf, pin, ICE_DPLL_PIN_TYPE_SOFTWARE, NULL); if (ret) diff --git a/drivers/net/ethernet/intel/ice/ice_dpll.h b/drivers/net/ethernet/intel/ice/ice_dpll.h index ae42cdea0ee1..8678575359b9 100644 --- a/drivers/net/ethernet/intel/ice/ice_dpll.h +++ b/drivers/net/ethernet/intel/ice/ice_dpll.h @@ -8,6 +8,22 @@ #define ICE_DPLL_RCLK_NUM_MAX 4 +#define ICE_CGU_R10 0x28 +#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) +#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) +#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) +#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) +#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) +#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) +#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) +#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) +#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) + +#define ICE_CGU_R11 0x2C +#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) + +#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3 + /** * enum ice_dpll_pin_sw - enumerate ice software pin indices: * @ICE_DPLL_PIN_SW_1_IDX: index of first SW pin @@ -157,19 +173,3 @@ static inline void ice_dpll_deinit(struct ice_pf *pf) { } #endif #endif - -#define ICE_CGU_R10 0x28 -#define ICE_CGU_R10_SYNCE_CLKO_SEL GENMASK(8, 5) -#define ICE_CGU_R10_SYNCE_CLKODIV_M1 GENMASK(13, 9) -#define ICE_CGU_R10_SYNCE_CLKODIV_LOAD BIT(14) -#define ICE_CGU_R10_SYNCE_DCK_RST BIT(15) -#define ICE_CGU_R10_SYNCE_ETHCLKO_SEL GENMASK(18, 16) -#define ICE_CGU_R10_SYNCE_ETHDIV_M1 GENMASK(23, 19) -#define ICE_CGU_R10_SYNCE_ETHDIV_LOAD BIT(24) -#define ICE_CGU_R10_SYNCE_DCK2_RST BIT(25) -#define ICE_CGU_R10_SYNCE_S_REF_CLK GENMASK(31, 27) - -#define ICE_CGU_R11 0x2C -#define ICE_CGU_R11_SYNCE_S_BYP_CLK GENMASK(6, 1) - -#define ICE_CGU_BYPASS_MUX_OFFSET_E825C 3 diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index e6a20af6f63d..f28416a707d7 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -3290,6 +3290,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring, tx_rings[i].desc = NULL; tx_rings[i].tx_buf = NULL; tx_rings[i].tstamp_ring = NULL; + clear_bit(ICE_TX_RING_FLAGS_TXTIME, tx_rings[i].flags); tx_rings[i].tx_tstamps = &pf->ptp.port.tx; err = ice_setup_tx_ring(&tx_rings[i]); if (err) { diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c index 689c6025ea82..837b71b7b2b7 100644 --- a/drivers/net/ethernet/intel/ice/ice_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_lib.c @@ -1412,9 +1412,9 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi) ring->count = vsi->num_tx_desc; ring->txq_teid = ICE_INVAL_TEID; if (dvm_ena) - ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG2; + set_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, ring->flags); else - ring->flags |= ICE_TX_FLAGS_RING_VLAN_L2TAG1; + set_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG1, ring->flags); WRITE_ONCE(vsi->tx_rings[i], ring); } diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index f93ee3b62391..c52c465280f7 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -1923,82 +1923,6 @@ static void ice_handle_mdd_event(struct ice_pf *pf) } /** - * ice_force_phys_link_state - Force the physical link state - * @vsi: VSI to force the physical link state to up/down - * @link_up: true/false indicates to set the physical link to up/down - * - * Force the physical link state by getting the current PHY capabilities from - * hardware and setting the PHY config based on the determined capabilities. If - * link changes a link event will be triggered because both the Enable Automatic - * Link Update and LESM Enable bits are set when setting the PHY capabilities. - * - * Returns 0 on success, negative on failure - */ -static int ice_force_phys_link_state(struct ice_vsi *vsi, bool link_up) -{ - struct ice_aqc_get_phy_caps_data *pcaps; - struct ice_aqc_set_phy_cfg_data *cfg; - struct ice_port_info *pi; - struct device *dev; - int retcode; - - if (!vsi || !vsi->port_info || !vsi->back) - return -EINVAL; - if (vsi->type != ICE_VSI_PF) - return 0; - - dev = ice_pf_to_dev(vsi->back); - - pi = vsi->port_info; - - pcaps = kzalloc_obj(*pcaps); - if (!pcaps) - return -ENOMEM; - - retcode = ice_aq_get_phy_caps(pi, false, ICE_AQC_REPORT_ACTIVE_CFG, pcaps, - NULL); - if (retcode) { - dev_err(dev, "Failed to get phy capabilities, VSI %d error %d\n", - vsi->vsi_num, retcode); - retcode = -EIO; - goto out; - } - - /* No change in link */ - if (link_up == !!(pcaps->caps & ICE_AQC_PHY_EN_LINK) && - link_up == !!(pi->phy.link_info.link_info & ICE_AQ_LINK_UP)) - goto out; - - /* Use the current user PHY configuration. The current user PHY - * configuration is initialized during probe from PHY capabilities - * software mode, and updated on set PHY configuration. - */ - cfg = kmemdup(&pi->phy.curr_user_phy_cfg, sizeof(*cfg), GFP_KERNEL); - if (!cfg) { - retcode = -ENOMEM; - goto out; - } - - cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT; - if (link_up) - cfg->caps |= ICE_AQ_PHY_ENA_LINK; - else - cfg->caps &= ~ICE_AQ_PHY_ENA_LINK; - - retcode = ice_aq_set_phy_cfg(&vsi->back->hw, pi, cfg, NULL); - if (retcode) { - dev_err(dev, "Failed to set phy config, VSI %d error %d\n", - vsi->vsi_num, retcode); - retcode = -EIO; - } - - kfree(cfg); -out: - kfree(pcaps); - return retcode; -} - -/** * ice_init_nvm_phy_type - Initialize the NVM PHY type * @pi: port info structure * @@ -2066,7 +1990,7 @@ static void ice_init_link_dflt_override(struct ice_port_info *pi) * first time media is available. The ICE_LINK_DEFAULT_OVERRIDE_PENDING state * is used to indicate that the user PHY cfg default override is initialized * and the PHY has not been configured with the default override settings. The - * state is set here, and cleared in ice_configure_phy the first time the PHY is + * state is set here, and cleared in ice_phy_cfg the first time the PHY is * configured. * * This function should be called only if the FW doesn't support default @@ -2172,14 +2096,18 @@ err_out: } /** - * ice_configure_phy - configure PHY + * ice_phy_cfg - configure PHY * @vsi: VSI of PHY + * @link_en: true/false indicates to set link to enable/disable * * Set the PHY configuration. If the current PHY configuration is the same as - * the curr_user_phy_cfg, then do nothing to avoid link flap. Otherwise - * configure the based get PHY capabilities for topology with media. + * the curr_user_phy_cfg and link_en hasn't changed, then do nothing to avoid + * link flap. Otherwise configure the PHY based get PHY capabilities for + * topology with media and link_en. + * + * Return: 0 on success, negative on failure */ -static int ice_configure_phy(struct ice_vsi *vsi) +static int ice_phy_cfg(struct ice_vsi *vsi, bool link_en) { struct device *dev = ice_pf_to_dev(vsi->back); struct ice_port_info *pi = vsi->port_info; @@ -2199,9 +2127,6 @@ static int ice_configure_phy(struct ice_vsi *vsi) phy->link_info.topo_media_conflict == ICE_AQ_LINK_TOPO_UNSUPP_MEDIA) return -EPERM; - if (test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, pf->flags)) - return ice_force_phys_link_state(vsi, true); - pcaps = kzalloc_obj(*pcaps); if (!pcaps) return -ENOMEM; @@ -2215,10 +2140,8 @@ static int ice_configure_phy(struct ice_vsi *vsi) goto done; } - /* If PHY enable link is configured and configuration has not changed, - * there's nothing to do - */ - if (pcaps->caps & ICE_AQC_PHY_EN_LINK && + /* Configuration has not changed. There's nothing to do. */ + if (link_en == !!(pcaps->caps & ICE_AQC_PHY_EN_LINK) && ice_phy_caps_equals_cfg(pcaps, &phy->curr_user_phy_cfg)) goto done; @@ -2282,8 +2205,12 @@ static int ice_configure_phy(struct ice_vsi *vsi) */ ice_cfg_phy_fc(pi, cfg, phy->curr_user_fc_req); - /* Enable link and link update */ - cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT | ICE_AQ_PHY_ENA_LINK; + /* Enable/Disable link and link update */ + cfg->caps |= ICE_AQ_PHY_ENA_AUTO_LINK_UPDT; + if (link_en) + cfg->caps |= ICE_AQ_PHY_ENA_LINK; + else + cfg->caps &= ~ICE_AQ_PHY_ENA_LINK; err = ice_aq_set_phy_cfg(&pf->hw, pi, cfg, NULL); if (err) @@ -2336,7 +2263,7 @@ static void ice_check_media_subtask(struct ice_pf *pf) test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, vsi->back->flags)) return; - err = ice_configure_phy(vsi); + err = ice_phy_cfg(vsi, true); if (!err) clear_bit(ICE_FLAG_NO_MEDIA, pf->flags); @@ -4892,9 +4819,15 @@ static int ice_init_link(struct ice_pf *pf) if (!test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, pf->flags)) { struct ice_vsi *vsi = ice_get_main_vsi(pf); + struct ice_link_default_override_tlv *ldo; + bool link_en; + + ldo = &pf->link_dflt_override; + link_en = !(ldo->options & + ICE_LINK_OVERRIDE_AUTO_LINK_DIS); if (vsi) - ice_configure_phy(vsi); + ice_phy_cfg(vsi, link_en); } } else { set_bit(ICE_FLAG_NO_MEDIA, pf->flags); @@ -5312,6 +5245,8 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent) return err; } + ice_init_dev_hw(pf); + adapter = ice_adapter_get(pdev); if (IS_ERR(adapter)) { err = PTR_ERR(adapter); @@ -8111,7 +8046,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc) ctx->info.q_opt_rss |= FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc); ctx->info.q_opt_tc = vsi->info.q_opt_tc; - ctx->info.q_opt_flags = vsi->info.q_opt_rss; + ctx->info.q_opt_flags = vsi->info.q_opt_flags; err = ice_update_vsi(hw, vsi->idx, ctx, NULL); if (err) { @@ -9707,7 +9642,7 @@ int ice_open_internal(struct net_device *netdev) } } - err = ice_configure_phy(vsi); + err = ice_phy_cfg(vsi, true); if (err) { netdev_err(netdev, "Failed to set physical link up, error %d\n", err); @@ -9748,7 +9683,7 @@ int ice_stop(struct net_device *netdev) } if (test_bit(ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA, vsi->back->flags)) { - int link_err = ice_force_phys_link_state(vsi, false); + int link_err = ice_phy_cfg(vsi, false); if (link_err) { if (link_err == -ENOMEDIUM) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c index 6cb0cf7a9891..36df742c326c 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp.c @@ -2710,7 +2710,7 @@ static bool ice_any_port_has_timestamps(struct ice_pf *pf) bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf) { struct ice_hw *hw = &pf->hw; - unsigned int i; + int ret; /* Check software indicator */ switch (pf->ptp.tx_interrupt_mode) { @@ -2731,16 +2731,19 @@ bool ice_ptp_tx_tstamps_pending(struct ice_pf *pf) } /* Check hardware indicator */ - for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) { - u64 tstamp_ready = 0; - int err; - - err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready); - if (err || tstamp_ready) - return true; + ret = ice_check_phy_tx_tstamp_ready(hw); + if (ret < 0) { + dev_dbg(ice_pf_to_dev(pf), "Unable to read PHY Tx timestamp ready bitmap, err %d\n", + ret); + /* Stop triggering IRQs if we're unable to read PHY */ + return false; } - return false; + /* ice_check_phy_tx_tstamp_ready() returns 1 if there are timestamps + * available, 0 if there are no waiting timestamps, and a negative + * value if there was an error (which we checked for above). + */ + return ret > 0; } /** @@ -2824,8 +2827,7 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf) { struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; - bool trigger_oicr = false; - unsigned int i; + int ret; if (!pf->ptp.port.tx.has_ready_bitmap) return; @@ -2833,21 +2835,11 @@ static void ice_ptp_maybe_trigger_tx_interrupt(struct ice_pf *pf) if (!ice_pf_src_tmr_owned(pf)) return; - for (i = 0; i < ICE_GET_QUAD_NUM(hw->ptp.num_lports); i++) { - u64 tstamp_ready; - int err; - - err = ice_get_phy_tx_tstamp_ready(&pf->hw, i, &tstamp_ready); - if (!err && tstamp_ready) { - trigger_oicr = true; - break; - } - } - - if (trigger_oicr) { - /* Trigger a software interrupt, to ensure this data - * gets processed. - */ + ret = ice_check_phy_tx_tstamp_ready(hw); + if (ret < 0) { + dev_dbg(dev, "PTP periodic task unable to read PHY timestamp ready bitmap, err %d\n", + ret); + } else if (ret) { dev_dbg(dev, "PTP periodic task detected waiting timestamps. Triggering Tx timestamp interrupt now.\n"); wr32(hw, PFINT_OICR, PFINT_OICR_TSYN_TX_M); diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_consts.h b/drivers/net/ethernet/intel/ice/ice_ptp_consts.h index 19dddd9b53dd..4d298c27bfb2 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_consts.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_consts.h @@ -78,14 +78,14 @@ struct ice_eth56g_mac_reg_cfg eth56g_mac_cfg[NUM_ICE_ETH56G_LNK_SPD] = { .blktime = 0x666, /* 3.2 */ .tx_offset = { .serdes = 0x234c, /* 17.6484848 */ - .no_fec = 0x8e80, /* 71.25 */ + .no_fec = 0x93d9, /* 73 */ .fc = 0xb4a4, /* 90.32 */ .sfd = 0x4a4, /* 2.32 */ .onestep = 0x4ccd /* 38.4 */ }, .rx_offset = { .serdes = 0xffffeb27, /* -10.42424 */ - .no_fec = 0xffffcccd, /* -25.6 */ + .no_fec = 0xffffc7b6, /* -28 */ .fc = 0xfffc557b, /* -469.26 */ .sfd = 0x4a4, /* 2.32 */ .bs_ds = 0x32 /* 0.0969697 */ @@ -118,17 +118,17 @@ struct ice_eth56g_mac_reg_cfg eth56g_mac_cfg[NUM_ICE_ETH56G_LNK_SPD] = { .mktime = 0x147b, /* 10.24, only if RS-FEC enabled */ .tx_offset = { .serdes = 0xe1e, /* 7.0593939 */ - .no_fec = 0x3857, /* 28.17 */ + .no_fec = 0x4266, /* 33 */ .fc = 0x48c3, /* 36.38 */ - .rs = 0x8100, /* 64.5 */ + .rs = 0x8a00, /* 69 */ .sfd = 0x1dc, /* 0.93 */ .onestep = 0x1eb8 /* 15.36 */ }, .rx_offset = { .serdes = 0xfffff7a9, /* -4.1697 */ - .no_fec = 0xffffe71a, /* -12.45 */ + .no_fec = 0xffffe700, /* -12 */ .fc = 0xfffe894d, /* -187.35 */ - .rs = 0xfffff8cd, /* -3.6 */ + .rs = 0xfffff8cc, /* -3 */ .sfd = 0x1dc, /* 0.93 */ .bs_ds = 0x14 /* 0.0387879, RS-FEC 0 */ } diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 61c0a0d93ea8..24fb7a3e14d6 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -378,6 +378,31 @@ static void ice_ptp_cfg_sync_delay(const struct ice_hw *hw, u32 delay) */ /** + * ice_ptp_init_phc_e825c - Perform E825C specific PHC initialization + * @hw: pointer to HW struct + * + * Perform E825C-specific PTP hardware clock initialization steps. + * + * Return: 0 on success, or a negative error value on failure. + */ +static int ice_ptp_init_phc_e825c(struct ice_hw *hw) +{ + int err; + + /* Soft reset all ports, to ensure everything is at a clean state */ + for (int port = 0; port < hw->ptp.num_lports; port++) { + err = ice_ptp_phy_soft_reset_eth56g(hw, port); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to soft reset port %d, err %d\n", + port, err); + return err; + } + } + + return 0; +} + +/** * ice_ptp_get_dest_dev_e825 - get destination PHY for given port number * @hw: pointer to the HW struct * @port: destination port @@ -1847,6 +1872,8 @@ static int ice_phy_cfg_mac_eth56g(struct ice_hw *hw, u8 port) * @ena: enable or disable interrupt * @threshold: interrupt threshold * + * The threshold cannot be 0 while the interrupt is enabled. + * * Configure TX timestamp interrupt for the specified port * * Return: @@ -1858,19 +1885,45 @@ int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold) int err; u32 val; + if (ena && !threshold) + return -EINVAL; + err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, &val); if (err) return err; + val &= ~PHY_TS_INT_CONFIG_ENA_M; if (ena) { - val |= PHY_TS_INT_CONFIG_ENA_M; val &= ~PHY_TS_INT_CONFIG_THRESHOLD_M; val |= FIELD_PREP(PHY_TS_INT_CONFIG_THRESHOLD_M, threshold); - } else { - val &= ~PHY_TS_INT_CONFIG_ENA_M; + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, + val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, + "Failed to update 'threshold' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n", + port, !!ena, threshold); + return err; + } + val |= PHY_TS_INT_CONFIG_ENA_M; + } + + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, + "Failed to update 'ena' PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n", + port, !!ena, threshold); + return err; } - return ice_write_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, val); + err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_TS_INT_CONFIG, &val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, + "Failed to read PHY_REG_TS_INT_CONFIG port=%u ena=%u threshold=%u\n", + port, !!ena, threshold); + return err; + } + + return 0; } /** @@ -2116,6 +2169,35 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) } /** + * ice_check_phy_tx_tstamp_ready_eth56g - Check Tx memory status for all ports + * @hw: pointer to the HW struct + * + * Check the PHY_REG_TX_MEMORY_STATUS for all ports. A set bit indicates + * a waiting timestamp. + * + * Return: 1 if any port has at least one timestamp ready bit set, + * 0 otherwise, and a negative error code if unable to read the bitmap. + */ +static int ice_check_phy_tx_tstamp_ready_eth56g(struct ice_hw *hw) +{ + int port; + + for (port = 0; port < hw->ptp.num_lports; port++) { + u64 tstamp_ready; + int err; + + err = ice_get_phy_tx_tstamp_ready(hw, port, &tstamp_ready); + if (err) + return err; + + if (tstamp_ready) + return 1; + } + + return 0; +} + +/** * ice_ptp_read_tx_hwtstamp_status_eth56g - Get TX timestamp status * @hw: pointer to the HW struct * @ts_status: the timestamp mask pointer @@ -2137,13 +2219,19 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status) *ts_status = 0; for (phy = 0; phy < params->num_phys; phy++) { + u8 port; int err; - err = ice_read_phy_eth56g(hw, phy, PHY_PTP_INT_STATUS, &status); + /* ice_read_phy_eth56g expects a port index, so use the first + * port of the PHY + */ + port = phy * hw->ptp.ports_per_phy; + + err = ice_read_phy_eth56g(hw, port, PHY_PTP_INT_STATUS, &status); if (err) return err; - *ts_status |= (status & mask) << (phy * hw->ptp.ports_per_phy); + *ts_status |= (status & mask) << port; } ice_debug(hw, ICE_DBG_PTP, "PHY interrupt err: %x\n", *ts_status); @@ -2152,6 +2240,69 @@ int ice_ptp_read_tx_hwtstamp_status_eth56g(struct ice_hw *hw, u32 *ts_status) } /** + * ice_ptp_phy_soft_reset_eth56g - Perform a PHY soft reset on ETH56G + * @hw: pointer to the HW structure + * @port: PHY port number + * + * Trigger a soft reset of the ETH56G PHY by toggling the soft reset + * bit in the PHY global register. The reset sequence consists of: + * 1. Clearing the soft reset bit + * 2. Asserting the soft reset bit + * 3. Clearing the soft reset bit again + * + * Short delays are inserted between each step to allow the hardware + * to settle. This provides a controlled way to reinitialize the PHY + * without requiring a full device reset. + * + * Return: 0 on success, or a negative error code on failure when + * reading or writing the PHY register. + */ +int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port) +{ + u32 global_val; + int err; + + err = ice_read_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, &global_val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to read PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; + } + + global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M; + ice_debug(hw, ICE_DBG_PTP, "Clearing soft reset bit for port %d, val: 0x%x\n", + port, global_val); + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; + } + + usleep_range(5000, 6000); + + global_val |= PHY_REG_GLOBAL_SOFT_RESET_M; + ice_debug(hw, ICE_DBG_PTP, "Set soft reset bit for port %d, val: 0x%x\n", + port, global_val); + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val); + if (err) { + ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; + } + usleep_range(5000, 6000); + + global_val &= ~PHY_REG_GLOBAL_SOFT_RESET_M; + ice_debug(hw, ICE_DBG_PTP, "Clear soft reset bit for port %d, val: 0x%x\n", + port, global_val); + err = ice_write_ptp_reg_eth56g(hw, port, PHY_REG_GLOBAL, global_val); + if (err) + ice_debug(hw, ICE_DBG_PTP, "Failed to write PHY_REG_GLOBAL for port %d, err %d\n", + port, err); + return err; +} + +/** * ice_get_phy_tx_tstamp_ready_eth56g - Read the Tx memory status register * @hw: pointer to the HW struct * @port: the PHY port to read from @@ -4203,6 +4354,35 @@ ice_get_phy_tx_tstamp_ready_e82x(struct ice_hw *hw, u8 quad, u64 *tstamp_ready) } /** + * ice_check_phy_tx_tstamp_ready_e82x - Check Tx memory status for all quads + * @hw: pointer to the HW struct + * + * Check the Q_REG_TX_MEMORY_STATUS for all quads. A set bit indicates + * a waiting timestamp. + * + * Return: 1 if any quad has at least one timestamp ready bit set, + * 0 otherwise, and a negative error value if unable to read the bitmap. + */ +static int ice_check_phy_tx_tstamp_ready_e82x(struct ice_hw *hw) +{ + int quad; + + for (quad = 0; quad < ICE_GET_QUAD_NUM(hw->ptp.num_lports); quad++) { + u64 tstamp_ready; + int err; + + err = ice_get_phy_tx_tstamp_ready(hw, quad, &tstamp_ready); + if (err) + return err; + + if (tstamp_ready) + return 1; + } + + return 0; +} + +/** * ice_phy_cfg_intr_e82x - Configure TX timestamp interrupt * @hw: pointer to the HW struct * @quad: the timestamp quad @@ -4755,6 +4935,23 @@ ice_get_phy_tx_tstamp_ready_e810(struct ice_hw *hw, u8 port, u64 *tstamp_ready) return 0; } +/** + * ice_check_phy_tx_tstamp_ready_e810 - Check Tx memory status register + * @hw: pointer to the HW struct + * + * The E810 devices do not have a Tx memory status register. Note this is + * intentionally different behavior from ice_get_phy_tx_tstamp_ready_e810 + * which always says that all bits are ready. This function is called in cases + * where code will trigger interrupts if timestamps are waiting, and should + * not be called for E810 hardware. + * + * Return: 0. + */ +static int ice_check_phy_tx_tstamp_ready_e810(struct ice_hw *hw) +{ + return 0; +} + /* E810 SMA functions * * The following functions operate specifically on E810 hardware and are used @@ -5010,6 +5207,21 @@ static void ice_get_phy_tx_tstamp_ready_e830(const struct ice_hw *hw, u8 port, } /** + * ice_check_phy_tx_tstamp_ready_e830 - Check Tx memory status register + * @hw: pointer to the HW struct + * + * Return: 1 if the device has waiting timestamps, 0 otherwise. + */ +static int ice_check_phy_tx_tstamp_ready_e830(struct ice_hw *hw) +{ + u64 tstamp_ready; + + ice_get_phy_tx_tstamp_ready_e830(hw, 0, &tstamp_ready); + + return !!tstamp_ready; +} + +/** * ice_ptp_init_phy_e830 - initialize PHY parameters * @ptp: pointer to the PTP HW struct */ @@ -5381,8 +5593,8 @@ int ice_ptp_write_incval_locked(struct ice_hw *hw, u64 incval) */ int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj) { + int err = 0; u8 tmr_idx; - int err; tmr_idx = hw->func_caps.ts_func_info.tmr_index_owned; @@ -5399,8 +5611,8 @@ int ice_ptp_adj_clock(struct ice_hw *hw, s32 adj) err = ice_ptp_prep_phy_adj_e810(hw, adj); break; case ICE_MAC_E830: - /* E830 sync PHYs automatically after setting GLTSYN_SHADJ */ - return 0; + /* E830 sync PHYs automatically after setting cmd register */ + break; case ICE_MAC_GENERIC: err = ice_ptp_prep_phy_adj_e82x(hw, adj); break; @@ -5564,7 +5776,7 @@ int ice_ptp_init_phc(struct ice_hw *hw) case ICE_MAC_GENERIC: return ice_ptp_init_phc_e82x(hw); case ICE_MAC_GENERIC_3K_E825: - return 0; + return ice_ptp_init_phc_e825c(hw); default: return -EOPNOTSUPP; } @@ -5602,6 +5814,33 @@ int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready) } /** + * ice_check_phy_tx_tstamp_ready - Check PHY Tx timestamp memory status + * @hw: pointer to the HW struct + * + * Check the PHY for Tx timestamp memory status on all ports. If you need to + * see individual timestamp status for each index, use + * ice_get_phy_tx_tstamp_ready() instead. + * + * Return: 1 if any port has timestamps available, 0 if there are no timestamps + * available, and a negative error code on failure. + */ +int ice_check_phy_tx_tstamp_ready(struct ice_hw *hw) +{ + switch (hw->mac_type) { + case ICE_MAC_E810: + return ice_check_phy_tx_tstamp_ready_e810(hw); + case ICE_MAC_E830: + return ice_check_phy_tx_tstamp_ready_e830(hw); + case ICE_MAC_GENERIC: + return ice_check_phy_tx_tstamp_ready_e82x(hw); + case ICE_MAC_GENERIC_3K_E825: + return ice_check_phy_tx_tstamp_ready_eth56g(hw); + default: + return -EOPNOTSUPP; + } +} + +/** * ice_cgu_get_pin_desc_e823 - get pin description array * @hw: pointer to the hw struct * @input: if request is done against input or output pin diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h index 9bfd3e79c580..1c9e77dbc770 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.h +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.h @@ -300,6 +300,7 @@ void ice_ptp_reset_ts_memory(struct ice_hw *hw); int ice_ptp_init_phc(struct ice_hw *hw); void ice_ptp_init_hw(struct ice_hw *hw); int ice_get_phy_tx_tstamp_ready(struct ice_hw *hw, u8 block, u64 *tstamp_ready); +int ice_check_phy_tx_tstamp_ready(struct ice_hw *hw); int ice_ptp_one_port_cmd(struct ice_hw *hw, u8 configured_port, enum ice_ptp_tmr_cmd configured_cmd); @@ -374,6 +375,7 @@ int ice_stop_phy_timer_eth56g(struct ice_hw *hw, u8 port, bool soft_reset); int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port); int ice_phy_cfg_intr_eth56g(struct ice_hw *hw, u8 port, bool ena, u8 threshold); int ice_phy_cfg_ptp_1step_eth56g(struct ice_hw *hw, u8 port); +int ice_ptp_phy_soft_reset_eth56g(struct ice_hw *hw, u8 port); #define ICE_ETH56G_NOMINAL_INCVAL 0x140000000ULL #define ICE_ETH56G_NOMINAL_PCS_REF_TUS 0x100000000ULL @@ -676,6 +678,9 @@ static inline u64 ice_get_base_incval(struct ice_hw *hw) #define ICE_P0_GNSS_PRSNT_N BIT(4) /* ETH56G PHY register addresses */ +#define PHY_REG_GLOBAL 0x0 +#define PHY_REG_GLOBAL_SOFT_RESET_M BIT(11) + /* Timestamp PHY incval registers */ #define PHY_REG_TIMETUS_L 0x8 #define PHY_REG_TIMETUS_U 0xC diff --git a/drivers/net/ethernet/intel/ice/ice_sf_eth.c b/drivers/net/ethernet/intel/ice/ice_sf_eth.c index 2cf04bc6edce..a730aa368c92 100644 --- a/drivers/net/ethernet/intel/ice/ice_sf_eth.c +++ b/drivers/net/ethernet/intel/ice/ice_sf_eth.c @@ -305,6 +305,8 @@ ice_sf_eth_activate(struct ice_dynamic_port *dyn_port, aux_dev_uninit: auxiliary_device_uninit(&sf_dev->adev); + return err; + sf_dev_free: kfree(sf_dev); xa_erase: diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index a2cd4cf37734..4ca1a0602307 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -190,9 +190,10 @@ void ice_free_tstamp_ring(struct ice_tx_ring *tx_ring) void ice_free_tx_tstamp_ring(struct ice_tx_ring *tx_ring) { ice_free_tstamp_ring(tx_ring); + clear_bit(ICE_TX_RING_FLAGS_TXTIME, tx_ring->flags); + smp_wmb(); /* order flag clear before pointer NULL */ kfree_rcu(tx_ring->tstamp_ring, rcu); - tx_ring->tstamp_ring = NULL; - tx_ring->flags &= ~ICE_TX_FLAGS_TXTIME; + WRITE_ONCE(tx_ring->tstamp_ring, NULL); } /** @@ -405,7 +406,7 @@ static int ice_alloc_tstamp_ring(struct ice_tx_ring *tx_ring) tx_ring->tstamp_ring = tstamp_ring; tstamp_ring->desc = NULL; tstamp_ring->count = ice_calc_ts_ring_count(tx_ring); - tx_ring->flags |= ICE_TX_FLAGS_TXTIME; + set_bit(ICE_TX_RING_FLAGS_TXTIME, tx_ring->flags); return 0; } @@ -1521,13 +1522,20 @@ ice_tx_map(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first, return; if (ice_is_txtime_cfg(tx_ring)) { - struct ice_tstamp_ring *tstamp_ring = tx_ring->tstamp_ring; - u32 tstamp_count = tstamp_ring->count; - u32 j = tstamp_ring->next_to_use; + struct ice_tstamp_ring *tstamp_ring; + u32 tstamp_count, j; struct ice_ts_desc *ts_desc; struct timespec64 ts; u32 tstamp; + smp_rmb(); /* order flag read before pointer read */ + tstamp_ring = READ_ONCE(tx_ring->tstamp_ring); + if (unlikely(!tstamp_ring)) + goto ring_kick; + + tstamp_count = tstamp_ring->count; + j = tstamp_ring->next_to_use; + ts = ktime_to_timespec64(first->skb->tstamp); tstamp = ts.tv_nsec >> ICE_TXTIME_CTX_RESOLUTION_128NS; @@ -1555,6 +1563,7 @@ ice_tx_map(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first, tstamp_ring->next_to_use = j; writel_relaxed(j, tstamp_ring->tail); } else { +ring_kick: writel_relaxed(i, tx_ring->tail); } return; @@ -1814,7 +1823,7 @@ ice_tx_prepare_vlan_flags(struct ice_tx_ring *tx_ring, struct ice_tx_buf *first) */ if (skb_vlan_tag_present(skb)) { first->vid = skb_vlan_tag_get(skb); - if (tx_ring->flags & ICE_TX_FLAGS_RING_VLAN_L2TAG2) + if (test_bit(ICE_TX_RING_FLAGS_VLAN_L2TAG2, tx_ring->flags)) first->tx_flags |= ICE_TX_FLAGS_HW_OUTER_SINGLE_VLAN; else first->tx_flags |= ICE_TX_FLAGS_HW_VLAN; @@ -2158,6 +2167,9 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) ice_trace(xmit_frame_ring, tx_ring, skb); + /* record the location of the first descriptor for this packet */ + first = &tx_ring->tx_buf[tx_ring->next_to_use]; + count = ice_xmit_desc_count(skb); if (ice_chk_linearize(skb, count)) { if (__skb_linearize(skb)) @@ -2183,8 +2195,6 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) offload.tx_ring = tx_ring; - /* record the location of the first descriptor for this packet */ - first = &tx_ring->tx_buf[tx_ring->next_to_use]; first->skb = skb; first->type = ICE_TX_BUF_SKB; first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN); @@ -2249,6 +2259,7 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) out_drop: ice_trace(xmit_frame_ring_drop, tx_ring, skb); dev_kfree_skb_any(skb); + first->type = ICE_TX_BUF_EMPTY; return NETDEV_TX_OK; } diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h index b6547e1b7c42..5e517f219379 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.h +++ b/drivers/net/ethernet/intel/ice/ice_txrx.h @@ -212,6 +212,14 @@ enum ice_rx_dtype { ICE_RX_DTYPE_SPLIT_ALWAYS = 2, }; +enum ice_tx_ring_flags { + ICE_TX_RING_FLAGS_XDP, + ICE_TX_RING_FLAGS_VLAN_L2TAG1, + ICE_TX_RING_FLAGS_VLAN_L2TAG2, + ICE_TX_RING_FLAGS_TXTIME, + ICE_TX_RING_FLAGS_NBITS, +}; + struct ice_pkt_ctx { u64 cached_phctime; __be16 vlan_proto; @@ -352,11 +360,7 @@ struct ice_tx_ring { u16 count; /* Number of descriptors */ u16 q_index; /* Queue number of ring */ - u8 flags; -#define ICE_TX_FLAGS_RING_XDP BIT(0) -#define ICE_TX_FLAGS_RING_VLAN_L2TAG1 BIT(1) -#define ICE_TX_FLAGS_RING_VLAN_L2TAG2 BIT(2) -#define ICE_TX_FLAGS_TXTIME BIT(3) + DECLARE_BITMAP(flags, ICE_TX_RING_FLAGS_NBITS); struct xsk_buff_pool *xsk_pool; @@ -398,7 +402,7 @@ static inline bool ice_ring_ch_enabled(struct ice_tx_ring *ring) static inline bool ice_ring_is_xdp(struct ice_tx_ring *ring) { - return !!(ring->flags & ICE_TX_FLAGS_RING_XDP); + return test_bit(ICE_TX_RING_FLAGS_XDP, ring->flags); } enum ice_container_type { diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.c b/drivers/net/ethernet/intel/ice/ice_vf_lib.c index 772f6b07340d..b1f46707dcc0 100644 --- a/drivers/net/ethernet/intel/ice/ice_vf_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.c @@ -804,7 +804,12 @@ void ice_reset_all_vfs(struct ice_pf *pf) ice_vf_ctrl_invalidate_vsi(vf); ice_vf_pre_vsi_rebuild(vf); - ice_vf_rebuild_vsi(vf); + if (ice_vf_rebuild_vsi(vf)) { + dev_err(dev, "VF %u VSI rebuild failed, leaving VF disabled\n", + vf->vf_id); + mutex_unlock(&vf->cfg_lock); + continue; + } ice_vf_post_vsi_rebuild(vf); ice_eswitch_attach_vf(pf, vf); diff --git a/drivers/net/ethernet/intel/idpf/idpf_idc.c b/drivers/net/ethernet/intel/idpf/idpf_idc.c index 7e4f4ac92653..b7d6b08fc89e 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_idc.c +++ b/drivers/net/ethernet/intel/idpf/idpf_idc.c @@ -90,7 +90,10 @@ static int idpf_plug_vport_aux_dev(struct iidc_rdma_core_dev_info *cdev_info, return 0; err_aux_dev_add: + ida_free(&idpf_idc_ida, adev->id); + vdev_info->adev = NULL; auxiliary_device_uninit(adev); + return ret; err_aux_dev_init: ida_free(&idpf_idc_ida, adev->id); err_ida_alloc: @@ -228,7 +231,10 @@ static int idpf_plug_core_aux_dev(struct iidc_rdma_core_dev_info *cdev_info) return 0; err_aux_dev_add: + ida_free(&idpf_idc_ida, adev->id); + cdev_info->adev = NULL; auxiliary_device_uninit(adev); + return ret; err_aux_dev_init: ida_free(&idpf_idc_ida, adev->id); err_ida_alloc: diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c index eec91c4f0a75..4a51d2727547 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c @@ -952,6 +952,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto free_ptp; } + spin_lock_init(&adapter->ptp->read_dev_clk_lock); + err = idpf_ptp_create_clock(adapter); if (err) goto free_ptp; @@ -977,8 +979,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto remove_clock; } - spin_lock_init(&adapter->ptp->read_dev_clk_lock); - pci_dbg(adapter->pdev, "PTP init successful\n"); return 0; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c index 3debf2fae1a4..6f13296303cb 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c @@ -249,34 +249,21 @@ DEFINE_SHOW_ATTRIBUTE(npc_defrag); int npc_cn20k_debugfs_init(struct rvu *rvu) { struct npc_priv_t *npc_priv = npc_priv_get(); - struct dentry *npc_dentry; - npc_dentry = debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_mcam_layout_fops); + debugfs_create_file("mcam_layout", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_mcam_layout_fops); - if (!npc_dentry) - return -EFAULT; + debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc, + rvu, &npc_mcam_default_fops); - npc_dentry = debugfs_create_file("mcam_default", 0444, rvu->rvu_dbg.npc, - rvu, &npc_mcam_default_fops); + debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_vidx2idx_map_fops); - if (!npc_dentry) - return -EFAULT; + debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_idx2vidx_map_fops); - npc_dentry = debugfs_create_file("vidx2idx", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_vidx2idx_map_fops); - if (!npc_dentry) - return -EFAULT; - - npc_dentry = debugfs_create_file("idx2vidx", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_idx2vidx_map_fops); - if (!npc_dentry) - return -EFAULT; - - npc_dentry = debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc, - npc_priv, &npc_defrag_fops); - if (!npc_dentry) - return -EFAULT; + debugfs_create_file("defrag", 0444, rvu->rvu_dbg.npc, + npc_priv, &npc_defrag_fops); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c index 7291fdb89b03..6b3f453fd500 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c @@ -798,7 +798,7 @@ program_mkex_extr: iounmap(mkex_prfl_addr); } -void +int npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, int index, bool enable) { @@ -808,7 +808,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, u64 cfg, hw_prio; u8 kw_type; - npc_mcam_idx_2_key_type(rvu, index, &kw_type); + if (index < 0 || index >= mcam->total_entries) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, index, &kw_type)) + return -EINVAL; + if (kw_type == NPC_MCAM_KEY_X2) { cfg = rvu_read64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, @@ -819,7 +824,7 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, rvu_write64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), cfg); - return; + return 0; } /* For NPC_CN20K_MCAM_KEY_X4 keys, both the banks @@ -836,10 +841,12 @@ npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), cfg); } + + return 0; } -void -npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index) +static void +npc_clear_x2_entry(struct rvu *rvu, int blkaddr, int bank, int index) { rvu_write64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1), @@ -873,6 +880,33 @@ npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int bank, int index) NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(index, bank), 0); } +int +npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int mcam_idx) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + int bank = npc_get_bank(mcam, mcam_idx); + u8 kw_type; + int index; + + if (npc_mcam_idx_2_key_type(rvu, mcam_idx, &kw_type)) + return -EINVAL; + + index = mcam_idx & (mcam->banksize - 1); + + if (kw_type == NPC_MCAM_KEY_X2) { + npc_clear_x2_entry(rvu, blkaddr, bank, index); + return 0; + } + + /* For NPC_MCAM_KEY_X4 keys, both the banks + * need to be programmed with the same value. + */ + for (bank = 0; bank < mcam->banks_per_entry; bank++) + npc_clear_x2_entry(rvu, blkaddr, bank, index); + + return 0; +} + static void npc_cn20k_get_keyword(struct cn20k_mcam_entry *entry, int idx, u64 *cam0, u64 *cam1) { @@ -1014,48 +1048,27 @@ static void npc_cn20k_config_kw_x4(struct rvu *rvu, struct npc_mcam *mcam, kw, req_kw_type); } -static void -npc_cn20k_set_mcam_bank_cfg(struct rvu *rvu, int blkaddr, int mcam_idx, - int bank, u8 kw_type, bool enable, u8 hw_prio) -{ - struct npc_mcam *mcam = &rvu->hw->mcam; - u64 bank_cfg; - - bank_cfg = (u64)hw_prio << 24; - if (enable) - bank_cfg |= 0x1; - - if (kw_type == NPC_MCAM_KEY_X2) { - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), - bank_cfg); - return; - } - - /* For NPC_MCAM_KEY_X4 keys, both the banks - * need to be programmed with the same value. - */ - for (bank = 0; bank < mcam->banks_per_entry; bank++) { - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), - bank_cfg); - } -} - -void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, - u8 intf, struct cn20k_mcam_entry *entry, - bool enable, u8 hw_prio, u8 req_kw_type) +int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, + u8 intf, struct cn20k_mcam_entry *entry, + bool enable, u8 hw_prio, u8 req_kw_type) { struct npc_mcam *mcam = &rvu->hw->mcam; int mcam_idx = index % mcam->banksize; int bank = index / mcam->banksize; + u64 bank_cfg = (u64)hw_prio << 24; int kw = 0; u8 kw_type; + if (index < 0 || index >= mcam->total_entries) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, index, &kw_type)) + return -EINVAL; + /* Disable before mcam entry update */ - npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false); + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, false)) + return -EINVAL; - npc_mcam_idx_2_key_type(rvu, index, &kw_type); /* CAM1 takes the comparison value and * CAM0 specifies match for a bit in key being '0' or '1' or 'dontcare'. * CAM1<n> = 0 & CAM0<n> = 1 => match if key<n> = 0 @@ -1064,7 +1077,7 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, */ if (kw_type == NPC_MCAM_KEY_X2) { /* Clear mcam entry to avoid writes being suppressed by NPC */ - npc_cn20k_clear_mcam_entry(rvu, blkaddr, bank, mcam_idx); + npc_clear_x2_entry(rvu, blkaddr, bank, mcam_idx); npc_cn20k_config_kw_x2(rvu, mcam, blkaddr, mcam_idx, intf, entry, bank, kw_type, kw, req_kw_type); @@ -1085,44 +1098,55 @@ void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, bank, 1), entry->vtag_action); - goto set_cfg; - } - - /* Clear mcam entry to avoid writes being suppressed by NPC */ - npc_cn20k_clear_mcam_entry(rvu, blkaddr, 0, mcam_idx); - npc_cn20k_clear_mcam_entry(rvu, blkaddr, 1, mcam_idx); - npc_cn20k_config_kw_x4(rvu, mcam, blkaddr, - mcam_idx, intf, entry, - kw_type, req_kw_type); - for (bank = 0; bank < mcam->banks_per_entry; bank++) { - /* Set 'action' */ + /* Set HW priority */ rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, - bank, 0), - entry->action); + NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), + bank_cfg); - /* Set TAG 'action' */ - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, - bank, 1), - entry->vtag_action); + } else { + /* Clear mcam entry to avoid writes being suppressed by NPC */ + npc_clear_x2_entry(rvu, blkaddr, 0, mcam_idx); + npc_clear_x2_entry(rvu, blkaddr, 1, mcam_idx); - /* Set 'action2' for inline receive */ - rvu_write64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, - bank, 2), - entry->action2); + npc_cn20k_config_kw_x4(rvu, mcam, blkaddr, + mcam_idx, intf, entry, + kw_type, req_kw_type); + for (bank = 0; bank < mcam->banks_per_entry; bank++) { + /* Set 'action' */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, + bank, 0), + entry->action); + + /* Set TAG 'action' */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, + bank, 1), + entry->vtag_action); + + /* Set 'action2' for inline receive */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(mcam_idx, + bank, 2), + entry->action2); + + /* Set HW priority */ + rvu_write64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_CFG_EXT(mcam_idx, bank), + bank_cfg); + } } -set_cfg: /* TODO: */ /* PF installing VF rule */ - npc_cn20k_set_mcam_bank_cfg(rvu, blkaddr, mcam_idx, bank, - kw_type, enable, hw_prio); + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable)) + return -EINVAL; + + return 0; } -void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) +int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) { struct npc_mcam *mcam = &rvu->hw->mcam; u64 cfg, sreg, dreg, soff, doff; @@ -1130,12 +1154,20 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) int bank, i, sb, db; int dbank, sbank; + if (src >= mcam->total_entries || dest >= mcam->total_entries) + return -EINVAL; + dbank = npc_get_bank(mcam, dest); sbank = npc_get_bank(mcam, src); - npc_mcam_idx_2_key_type(rvu, src, &src_kwtype); - npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype); + + if (npc_mcam_idx_2_key_type(rvu, src, &src_kwtype)) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, dest, &dest_kwtype)) + return -EINVAL; + if (src_kwtype != dest_kwtype) - return; + return -EINVAL; src &= (mcam->banksize - 1); dest &= (mcam->banksize - 1); @@ -1170,6 +1202,8 @@ void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, u16 src, u16 dest) if (src_kwtype == NPC_MCAM_KEY_X2) break; } + + return 0; } static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx, @@ -1179,21 +1213,37 @@ static void npc_cn20k_fill_entryword(struct cn20k_mcam_entry *entry, int idx, entry->kw_mask[idx] = cam1 ^ cam0; } -void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, - struct cn20k_mcam_entry *entry, - u8 *intf, u8 *ena, u8 *hw_prio) +int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, + struct cn20k_mcam_entry *entry, + u8 *intf, u8 *ena, u8 *hw_prio) { struct npc_mcam *mcam = &rvu->hw->mcam; u64 cam0, cam1, bank_cfg, cfg; int kw = 0, bank; u8 kw_type; - npc_mcam_idx_2_key_type(rvu, index, &kw_type); + if (index >= mcam->total_entries) + return -EINVAL; + + if (npc_mcam_idx_2_key_type(rvu, index, &kw_type)) + return -EINVAL; bank = npc_get_bank(mcam, index); index &= (mcam->banksize - 1); cfg = rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 0)); + entry->action = cfg; + + cfg = rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 1)); + entry->vtag_action = cfg; + + cfg = rvu_read64(rvu, blkaddr, + NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, bank, 2)); + entry->action2 = cfg; + + cfg = rvu_read64(rvu, blkaddr, NPC_AF_CN20K_MCAMEX_BANKX_CAMX_INTF_EXT(index, bank, 1)) & 3; *intf = cfg; @@ -1242,7 +1292,7 @@ void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, bank, 0)); npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1); - goto read_action; + return 0; } for (bank = 0; bank < mcam->banks_per_entry; bank++, kw = kw + 4) { @@ -1287,17 +1337,7 @@ void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, npc_cn20k_fill_entryword(entry, kw + 3, cam0, cam1); } -read_action: - /* 'action' is set to same value for both bank '0' and '1'. - * Hence, reading bank '0' should be enough. - */ - cfg = rvu_read64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 0)); - entry->action = cfg; - - cfg = rvu_read64(rvu, blkaddr, - NPC_AF_CN20K_MCAMEX_BANKX_ACTIONX_EXT(index, 0, 1)); - entry->vtag_action = cfg; + return 0; } int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu, @@ -1335,11 +1375,10 @@ int rvu_mbox_handler_npc_cn20k_mcam_write_entry(struct rvu *rvu, if (is_pffunc_af(req->hdr.pcifunc)) nix_intf = req->intf; - npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf, - &req->entry_data, req->enable_entry, - req->hw_prio, req->req_kw_type); + rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, req->entry, nix_intf, + &req->entry_data, req->enable_entry, + req->hw_prio, req->req_kw_type); - rc = 0; exit: mutex_unlock(&mcam->lock); return rc; @@ -1361,11 +1400,13 @@ int rvu_mbox_handler_npc_cn20k_mcam_read_entry(struct rvu *rvu, mutex_lock(&mcam->lock); rc = npc_mcam_verify_entry(mcam, pcifunc, req->entry); - if (!rc) - npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry, - &rsp->entry_data, &rsp->intf, - &rsp->enable, &rsp->hw_prio); + if (rc) + goto fail; + rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, req->entry, + &rsp->entry_data, &rsp->intf, + &rsp->enable, &rsp->hw_prio); +fail: mutex_unlock(&mcam->lock); return rc; } @@ -1375,11 +1416,13 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu, struct npc_mcam_alloc_and_write_entry_rsp *rsp) { struct rvu_pfvf *pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + struct npc_mcam_free_entry_req free_req = { 0 }; struct npc_mcam_alloc_entry_req entry_req; struct npc_mcam_alloc_entry_rsp entry_rsp; struct npc_mcam *mcam = &rvu->hw->mcam; u16 entry = NPC_MCAM_ENTRY_INVALID; - int blkaddr, rc; + struct msg_rsp free_rsp; + int blkaddr, rc, err; u8 nix_intf; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); @@ -1415,12 +1458,23 @@ int rvu_mbox_handler_npc_cn20k_mcam_alloc_and_write_entry(struct rvu *rvu, else nix_intf = pfvf->nix_rx_intf; - npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf, - &req->entry_data, req->enable_entry, - req->hw_prio, req->req_kw_type); + rc = npc_cn20k_config_mcam_entry(rvu, blkaddr, entry, nix_intf, + &req->entry_data, req->enable_entry, + req->hw_prio, req->req_kw_type); mutex_unlock(&mcam->lock); + if (rc) { + free_req.hdr.pcifunc = req->hdr.pcifunc; + free_req.entry = entry_rsp.entry; + err = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &free_rsp); + if (err) + dev_err(rvu->dev, + "%s: Error to free mcam idx %u\n", + __func__, entry_rsp.entry); + return rc; + } + rsp->entry = entry_rsp.entry; return 0; } @@ -1480,9 +1534,9 @@ int rvu_mbox_handler_npc_cn20k_read_base_steer_rule(struct rvu *rvu, read_entry: /* Read the mcam entry */ - npc_cn20k_read_mcam_entry(rvu, blkaddr, index, - &rsp->entry, &intf, - &enable, &hw_prio); + rc = npc_cn20k_read_mcam_entry(rvu, blkaddr, index, + &rsp->entry, &intf, + &enable, &hw_prio); mutex_unlock(&mcam->lock); out: return rc; @@ -2305,6 +2359,7 @@ err2: __npc_subbank_mark_free(rvu, sb); err1: kfree(save); + *alloc_cnt = 0; return rc; } @@ -3482,7 +3537,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, { int alloc_cnt1, alloc_cnt2; struct npc_subbank *sb; - int rc, sb_off, i; + int rc, sb_off, i, err; bool deleted; sb = &npc_priv.sb[f->idx]; @@ -3496,6 +3551,7 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, NPC_MCAM_LOWER_PRIO, false, cnt, save, cnt, true, &alloc_cnt1); + if (alloc_cnt1 < cnt) { rc = __npc_subbank_alloc(rvu, sb, NPC_MCAM_KEY_X2, sb->b1b, @@ -3511,15 +3567,17 @@ static int npc_defrag_alloc_free_slots(struct rvu *rvu, dev_err(rvu->dev, "%s: Failed to alloc cnt=%u alloc_cnt1=%u alloc_cnt2=%u\n", __func__, cnt, alloc_cnt1, alloc_cnt2); + rc = -ENOSPC; goto fail_free_alloc; } + return 0; fail_free_alloc: for (i = 0; i < alloc_cnt1 + alloc_cnt2; i++) { - rc = npc_mcam_idx_2_subbank_idx(rvu, save[i], - &sb, &sb_off); - if (rc) { + err = npc_mcam_idx_2_subbank_idx(rvu, save[i], + &sb, &sb_off); + if (err) { dev_err(rvu->dev, "%s: Error to find subbank for mcam idx=%u\n", __func__, save[i]); @@ -3565,9 +3623,10 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu, struct npc_defrag_node *v, int cnt, u16 *save) { + u16 new_midx, old_midx, vidx, target_pf; struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule, *tmp; int i, vidx_cnt, rc, sb_off; - u16 new_midx, old_midx, vidx; struct npc_subbank *sb; bool deleted; u16 pcifunc; @@ -3607,9 +3666,30 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu, NPC_AF_CN20K_MCAMEX_BANKX_STAT_EXT(midx, bank)); - npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false); - npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx); - npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true); + /* If bug happened during copy/enable mcam, then there is a bug in allocation + * algorithm itself. There is no point in rewinding and returning, as it + * will face further issue. Return error after printing error + */ + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, old_midx, false)) { + dev_err(rvu->dev, + "%s: Error happened while disabling old_mid=%u\n", + __func__, old_midx); + return -EFAULT; + } + + if (npc_cn20k_copy_mcam_entry(rvu, blkaddr, old_midx, new_midx)) { + dev_err(rvu->dev, + "%s: Error happened while copying old_midx=%u new_midx=%u\n", + __func__, old_midx, new_midx); + return -EFAULT; + } + + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, new_midx, true)) { + dev_err(rvu->dev, + "%s: Error happened while enabling new_mid=%u\n", + __func__, new_midx); + return -EFAULT; + } midx = new_midx % mcam->banksize; bank = new_midx / mcam->banksize; @@ -3665,8 +3745,21 @@ int npc_defrag_move_vdx_to_free(struct rvu *rvu, mcam->entry2pfvf_map[new_midx] = pcifunc; /* Counter is not preserved */ mcam->entry2cntr_map[new_midx] = new_midx; + target_pf = mcam->entry2target_pffunc[old_midx]; + mcam->entry2target_pffunc[new_midx] = target_pf; + mcam->entry2target_pffunc[old_midx] = NPC_MCAM_INVALID_MAP; + npc_mcam_set_bit(mcam, new_midx); + /* Note: list order is not functionally required for mcam_rules */ + list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) { + if (rule->entry != old_midx) + continue; + + rule->entry = new_midx; + break; + } + /* Mark as invalid */ v->vidx[vidx_cnt - i - 1] = -1; save[cnt - i - 1] = -1; @@ -3935,6 +4028,13 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, void *val; int i, j; + for (i = 0; i < ARRAY_SIZE(ptr); i++) { + if (!ptr[i]) + continue; + + *ptr[i] = USHRT_MAX; + } + if (!npc_priv.init_done) return 0; @@ -3950,7 +4050,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, npc_dft_rule_name[NPC_DFT_RULE_PROMISC_ID], pcifunc); - *ptr[0] = USHRT_MAX; return -ESRCH; } @@ -3970,7 +4069,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, npc_dft_rule_name[NPC_DFT_RULE_UCAST_ID], pcifunc); - *ptr[3] = USHRT_MAX; return -ESRCH; } @@ -3990,7 +4088,6 @@ int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, __func__, npc_dft_rule_name[i], pcifunc); - *ptr[j] = USHRT_MAX; continue; } @@ -4085,7 +4182,7 @@ int rvu_mbox_handler_npc_get_dft_rl_idxs(struct rvu *rvu, struct msg_req *req, return 0; } -static bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc) +bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc) { return is_pf_cgxmapped(rvu, rvu_get_pf(rvu->pdev, pcifunc)) || is_lbk_vf(rvu, pcifunc); @@ -4093,11 +4190,11 @@ static bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc) void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) { - struct npc_mcam_free_entry_req free_req = { 0 }; + struct npc_mcam *mcam = &rvu->hw->mcam; + u16 ptr[4] = {[0 ... 3] = USHRT_MAX}; + struct rvu_npc_mcam_rule *rule, *tmp; unsigned long index; - struct msg_rsp rsp; - u16 ptr[4]; - int rc, i; + int blkaddr, rc, i; void *map; if (!npc_priv.init_done) @@ -4155,14 +4252,43 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc) } free_rules: + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) + return; + for (int i = 0; i < 4; i++) { + if (ptr[i] == USHRT_MAX) + continue; - free_req.hdr.pcifunc = pcifunc; - free_req.all = 1; - rc = rvu_mbox_handler_npc_mcam_free_entry(rvu, &free_req, &rsp); - if (rc) - dev_err(rvu->dev, - "%s: Error deleting default entries (pcifunc=%#x\n", - __func__, pcifunc); + mutex_lock(&mcam->lock); + npc_mcam_clear_bit(mcam, ptr[i]); + mcam->entry2pfvf_map[ptr[i]] = NPC_MCAM_INVALID_MAP; + npc_cn20k_enable_mcam_entry(rvu, blkaddr, ptr[i], false); + mcam->entry2target_pffunc[ptr[i]] = 0x0; + mutex_unlock(&mcam->lock); + + rc = npc_cn20k_idx_free(rvu, &ptr[i], 1); + if (rc) { + /* Non recoverable error. Let us WARN and return. Keep system alive to + * enable debugging + */ + WARN(1, "%s Error deleting default entries (pcifunc=%#x) mcam_idx=%u\n", + __func__, pcifunc, ptr[i]); + return; + } + } + + mutex_lock(&mcam->lock); + list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) { + for (int i = 0; i < 4; i++) { + if (ptr[i] != rule->entry) + continue; + + list_del(&rule->list); + kfree(rule); + break; + } + } + mutex_unlock(&mcam->lock); } int npc_cn20k_dft_rules_alloc(struct rvu *rvu, u16 pcifunc) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h index 815d0b257a7e..3d5eb952cc07 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.h @@ -320,21 +320,21 @@ void npc_cn20k_dft_rules_free(struct rvu *rvu, u16 pcifunc); int npc_cn20k_dft_rules_idx_get(struct rvu *rvu, u16 pcifunc, u16 *bcast, u16 *mcast, u16 *promisc, u16 *ucast); -void npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, - u8 intf, struct cn20k_mcam_entry *entry, - bool enable, u8 hw_prio, u8 req_kw_type); -void npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, - int index, bool enable); -void npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, - u16 src, u16 dest); -void npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, - struct cn20k_mcam_entry *entry, u8 *intf, - u8 *ena, u8 *hw_prio); -void npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, - int bank, int index); +int npc_cn20k_config_mcam_entry(struct rvu *rvu, int blkaddr, int index, + u8 intf, struct cn20k_mcam_entry *entry, + bool enable, u8 hw_prio, u8 req_kw_type); +int npc_cn20k_enable_mcam_entry(struct rvu *rvu, int blkaddr, + int index, bool enable); +int npc_cn20k_copy_mcam_entry(struct rvu *rvu, int blkaddr, + u16 src, u16 dest); +int npc_cn20k_read_mcam_entry(struct rvu *rvu, int blkaddr, u16 index, + struct cn20k_mcam_entry *entry, u8 *intf, + u8 *ena, u8 *hw_prio); +int npc_cn20k_clear_mcam_entry(struct rvu *rvu, int blkaddr, int index); int npc_mcam_idx_2_key_type(struct rvu *rvu, u16 mcam_idx, u8 *key_type); u16 npc_cn20k_vidx2idx(u16 index); u16 npc_cn20k_idx2vidx(u16 idx); int npc_cn20k_defrag(struct rvu *rvu); +bool npc_is_cgx_or_lbk(struct rvu *rvu, u16 pcifunc); #endif /* NPC_CN20K_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index ef5b081162eb..f977734ae712 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -3577,6 +3577,9 @@ static int nix_update_mce_rule(struct rvu *rvu, u16 pcifunc, mcam_index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK, nixlf, type); + if (mcam_index < 0) + return -EINVAL; + err = nix_update_mce_list(rvu, pcifunc, mce_list, mce_idx, mcam_index, add); return err; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index c2ca5ed1d028..3c814d157ab9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -163,14 +163,35 @@ int npc_get_nixlf_mcam_index(struct npc_mcam *mcam, if (rc) return -EFAULT; + if (is_lbk_vf(rvu, pcifunc)) { + if (promisc == USHRT_MAX) + return -EINVAL; + return promisc; + } + + if (is_cgx_vf(rvu, pcifunc)) { + if (ucast == USHRT_MAX) + return -EINVAL; + + return ucast; + } + switch (type) { case NIXLF_BCAST_ENTRY: + if (bcast == USHRT_MAX) + return -EINVAL; return bcast; case NIXLF_ALLMULTI_ENTRY: + if (mcast == USHRT_MAX) + return -EINVAL; return mcast; case NIXLF_PROMISC_ENTRY: + if (promisc == USHRT_MAX) + return -EINVAL; return promisc; case NIXLF_UCAST_ENTRY: + if (ucast == USHRT_MAX) + return -EINVAL; return ucast; default: return -EINVAL; @@ -238,10 +259,10 @@ void npc_enable_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, int actbank = bank; if (is_cn20k(rvu->pdev)) { - if (index < 0 || index >= mcam->banksize * mcam->banks) - return; - - return npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable); + if (npc_cn20k_enable_mcam_entry(rvu, blkaddr, index, enable)) + dev_err(rvu->dev, "Error to %s mcam %u entry\n", + enable ? "enable" : "disable", index); + return; } index &= (mcam->banksize - 1); @@ -258,6 +279,13 @@ static void npc_clear_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, int bank = npc_get_bank(mcam, index); int actbank = bank; + if (is_cn20k(rvu->pdev)) { + if (npc_cn20k_clear_mcam_entry(rvu, blkaddr, index)) + dev_err(rvu->dev, "%s Failed to clear mcam %u\n", + __func__, index); + return; + } + index &= (mcam->banksize - 1); for (; bank < (actbank + mcam->banks_per_entry); bank++) { rvu_write64(rvu, blkaddr, @@ -424,6 +452,15 @@ static u64 npc_get_default_entry_action(struct rvu *rvu, struct npc_mcam *mcam, index = npc_get_nixlf_mcam_index(mcam, pf_func, nixlf, NIXLF_UCAST_ENTRY); + + if (index < 0) { + dev_err(rvu->dev, + "%s: failed to get ucast entry pcifunc:0x%x\n", + __func__, pf_func); + /* Action 0 is drop */ + return 0; + } + bank = npc_get_bank(mcam, index); index &= (mcam->banksize - 1); @@ -589,8 +626,8 @@ void npc_read_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, NPC_AF_MCAMEX_BANKX_CFG(src, sbank)) & 1; } -static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, - int blkaddr, u16 src, u16 dest) +static int npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, + int blkaddr, u16 src, u16 dest) { int dbank = npc_get_bank(mcam, dest); int sbank = npc_get_bank(mcam, src); @@ -630,6 +667,7 @@ static void npc_copy_mcam_entry(struct rvu *rvu, struct npc_mcam *mcam, NPC_AF_MCAMEX_BANKX_CFG(src, sbank)); rvu_write64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_CFG(dest, dbank), cfg); + return 0; } u64 npc_get_mcam_action(struct rvu *rvu, struct npc_mcam *mcam, @@ -689,6 +727,12 @@ void rvu_npc_install_ucast_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } /* Don't change the action if entry is already enabled * Otherwise RSS action may get overwritten. @@ -744,16 +788,38 @@ void rvu_npc_install_promisc_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_PROMISC_ENTRY); + /* In cn20k, default indexes are installed only for CGX mapped + * and lbk interfaces + */ if (is_cgx_vf(rvu, pcifunc)) index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK, nixlf, NIXLF_PROMISC_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get promisc entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + /* If the corresponding PF's ucast action is RSS, * use the same action for promisc also + * Please note that for lbk(s) "index" and "ucast_idx" + * will be same. */ - ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, - nixlf, NIXLF_UCAST_ENTRY); + if (is_lbk_vf(rvu, pcifunc)) + ucast_idx = index; + else + ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, + nixlf, NIXLF_UCAST_ENTRY); + if (ucast_idx < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast/promisc entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx)) *(u64 *)&action = npc_get_mcam_action(rvu, mcam, blkaddr, ucast_idx); @@ -827,6 +893,14 @@ void rvu_npc_enable_promisc_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_PROMISC_ENTRY); + + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get promisc entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); } @@ -867,6 +941,12 @@ void rvu_npc_install_bcast_match_entry(struct rvu *rvu, u16 pcifunc, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_BCAST_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get bcast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } if (!hw->cap.nix_rx_multicast) { /* Early silicon doesn't support pkt replication, @@ -931,12 +1011,25 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_ALLMULTI_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get mcast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } /* If the corresponding PF's ucast action is RSS, * use the same action for multicast entry also */ ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (ucast_idx < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + if (is_mcam_entry_enabled(rvu, mcam, blkaddr, ucast_idx)) *(u64 *)&action = npc_get_mcam_action(rvu, mcam, blkaddr, ucast_idx); @@ -1001,6 +1094,13 @@ void rvu_npc_enable_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_ALLMULTI_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get mcast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); } @@ -1113,8 +1213,12 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, index = mcam_index; } - if (index >= mcam->total_entries) + if (index < 0 || index >= mcam->total_entries) { + dev_err(rvu->dev, + "%s: Invalid mcam index, pcifunc=%#x\n", + __func__, pcifunc); return; + } bank = npc_get_bank(mcam, index); index &= (mcam->banksize - 1); @@ -1158,16 +1262,18 @@ void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, /* If PF's promiscuous entry is enabled, * Set RSS action for that entry as well */ - npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, - blkaddr, alg_idx); + if (index >= 0) + npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, + blkaddr, alg_idx); index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_ALLMULTI_ENTRY); /* If PF's allmulti entry is enabled, * Set RSS action for that entry as well */ - npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, - blkaddr, alg_idx); + if (index >= 0) + npc_update_rx_action_with_alg_idx(rvu, action, pfvf, index, + blkaddr, alg_idx); } } @@ -1180,12 +1286,22 @@ void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc, int index, blkaddr, mce_idx; struct rvu_pfvf *pfvf; + /* multicast pkt replication is not enabled for AF's VFs & SDP links */ + if (is_lbk_vf(rvu, pcifunc) || is_sdp_pfvf(rvu, pcifunc)) + return; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) return; index = npc_get_nixlf_mcam_index(mcam, pcifunc & ~RVU_PFVF_FUNC_MASK, nixlf, type); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get entry for pcifunc=%#x, type=%u\n", + __func__, pcifunc, type); + return; + } /* disable MCAM entry when packet replication is not supported by hw */ if (!hw->cap.nix_rx_multicast && !is_vf(pcifunc)) { @@ -1214,6 +1330,10 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, struct npc_mcam *mcam = &rvu->hw->mcam; int index, blkaddr; + /* only CGX or LBK interfaces have default entries */ + if (is_cn20k(rvu->pdev) && !npc_is_cgx_or_lbk(rvu, pcifunc)) + return; + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); if (blkaddr < 0) return; @@ -1223,6 +1343,12 @@ static void npc_enadis_default_entries(struct rvu *rvu, u16 pcifunc, pfvf->nix_rx_intf)) { index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (index < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } npc_enable_mcam_entry(rvu, mcam, blkaddr, index, enable); } @@ -2504,33 +2630,58 @@ void npc_mcam_clear_bit(struct npc_mcam *mcam, u16 index) static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam, int blkaddr, u16 pcifunc) { + u16 dft_idxs[NPC_DFT_RULE_MAX_ID] = {[0 ... NPC_DFT_RULE_MAX_ID - 1] = USHRT_MAX}; + bool cn20k_dft_rl; u16 index, cntr; int rc; + npc_cn20k_dft_rules_idx_get(rvu, pcifunc, + &dft_idxs[NPC_DFT_RULE_BCAST_ID], + &dft_idxs[NPC_DFT_RULE_MCAST_ID], + &dft_idxs[NPC_DFT_RULE_PROMISC_ID], + &dft_idxs[NPC_DFT_RULE_UCAST_ID]); + /* Scan all MCAM entries and free the ones mapped to 'pcifunc' */ for (index = 0; index < mcam->bmap_entries; index++) { - if (mcam->entry2pfvf_map[index] == pcifunc) { + if (mcam->entry2pfvf_map[index] != pcifunc) + continue; + + cn20k_dft_rl = false; + + if (is_cn20k(rvu->pdev)) { + if (dft_idxs[NPC_DFT_RULE_BCAST_ID] == index || + dft_idxs[NPC_DFT_RULE_MCAST_ID] == index || + dft_idxs[NPC_DFT_RULE_PROMISC_ID] == index || + dft_idxs[NPC_DFT_RULE_UCAST_ID] == index) { + cn20k_dft_rl = true; + } + } + + /* Disable the entry */ + npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); + + if (!cn20k_dft_rl) { mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP; /* Free the entry in bitmap */ npc_mcam_clear_bit(mcam, index); - /* Disable the entry */ - npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false); - - /* Update entry2counter mapping */ - cntr = mcam->entry2cntr_map[index]; - if (cntr != NPC_MCAM_INVALID_MAP) - npc_unmap_mcam_entry_and_cntr(rvu, mcam, - blkaddr, index, - cntr); mcam->entry2target_pffunc[index] = 0x0; - if (is_cn20k(rvu->pdev)) { - rc = npc_cn20k_idx_free(rvu, &index, 1); - if (rc) - dev_err(rvu->dev, - "Failed to free mcam idx=%u pcifunc=%#x\n", - index, pcifunc); - } } + + /* Update entry2counter mapping */ + cntr = mcam->entry2cntr_map[index]; + if (cntr != NPC_MCAM_INVALID_MAP) + npc_unmap_mcam_entry_and_cntr(rvu, mcam, + blkaddr, index, + cntr); + + if (!is_cn20k(rvu->pdev) || cn20k_dft_rl) + continue; + + rc = npc_cn20k_idx_free(rvu, &index, 1); + if (rc) + dev_err(rvu->dev, + "Failed to free mcam idx=%u pcifunc=%#x\n", + index, pcifunc); } } @@ -3266,7 +3417,10 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu, npc_enable_mcam_entry(rvu, mcam, blkaddr, new_entry, false); /* Copy rule from old entry to new entry */ - npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry); + if (npc_copy_mcam_entry(rvu, mcam, blkaddr, old_entry, new_entry)) { + rc = NPC_MCAM_INVALID_REQ; + break; + } /* Copy counter mapping, if any */ cntr = mcam->entry2cntr_map[old_entry]; @@ -3284,7 +3438,8 @@ int rvu_mbox_handler_npc_mcam_shift_entry(struct rvu *rvu, /* If shift has failed then report the failed index */ if (index != req->shift_count) { - rc = NPC_MCAM_PERM_DENIED; + if (!rc) + rc = NPC_MCAM_PERM_DENIED; rsp->failed_entry_idx = index; } @@ -3851,6 +4006,12 @@ int rvu_mbox_handler_npc_read_base_steer_rule(struct rvu *rvu, /* Read the default ucast entry if there is no pkt steering rule */ index = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (index < 0) { + mutex_unlock(&mcam->lock); + rc = NIX_AF_ERR_AF_LF_INVALID; + goto out; + } + read_entry: /* Read the mcam entry */ npc_read_mcam_entry(rvu, mcam, blkaddr, index, &rsp->entry, &intf, @@ -3924,6 +4085,12 @@ void rvu_npc_clear_ucast_entry(struct rvu *rvu, int pcifunc, int nixlf) ucast_idx = npc_get_nixlf_mcam_index(mcam, pcifunc, nixlf, NIXLF_UCAST_ENTRY); + if (ucast_idx < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for pcifunc=%#x\n", + __func__, pcifunc); + return; + } npc_enable_mcam_entry(rvu, mcam, blkaddr, ucast_idx, false); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index b45798d9fdab..6ae9cdcb608b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -1444,7 +1444,7 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, struct msg_rsp write_rsp; struct mcam_entry *entry; bool new = false; - u16 entry_index; + int entry_index; int err; installed_features = req->features; @@ -1477,6 +1477,14 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, if (req->default_rule) { entry_index = npc_get_nixlf_mcam_index(mcam, target, nixlf, NIXLF_UCAST_ENTRY); + + if (entry_index < 0) { + dev_err(rvu->dev, + "%s: Error to get ucast entry for target=%#x\n", + __func__, target); + return -EINVAL; + } + enable = is_mcam_entry_enabled(rvu, mcam, blkaddr, entry_index); } @@ -1980,13 +1988,15 @@ static int npc_update_dmac_value(struct rvu *rvu, int npcblkaddr, ether_addr_copy(rule->packet.dmac, pfvf->mac_addr); - if (is_cn20k(rvu->pdev)) - npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry, - cn20k_entry, &intf, - &enable, &hw_prio); - else + if (is_cn20k(rvu->pdev)) { + if (npc_cn20k_read_mcam_entry(rvu, npcblkaddr, rule->entry, + cn20k_entry, &intf, + &enable, &hw_prio)) + return -EINVAL; + } else { npc_read_mcam_entry(rvu, mcam, npcblkaddr, rule->entry, entry, &intf, &enable); + } npc_update_entry(rvu, NPC_DMAC, &mdata, ether_addr_to_u64(pfvf->mac_addr), 0, @@ -2038,8 +2048,12 @@ void npc_mcam_enable_flows(struct rvu *rvu, u16 target) continue; } - if (rule->vfvlan_cfg) - npc_update_dmac_value(rvu, blkaddr, rule, pfvf); + if (rule->vfvlan_cfg) { + if (npc_update_dmac_value(rvu, blkaddr, rule, pfvf)) + dev_err(rvu->dev, + "Update dmac failed for %u, target=%#x\n", + rule->entry, target); + } if (rule->rx_action.op == NIX_RX_ACTION_DEFAULT) { if (!def_ucast_rule) diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c index dd890f5d7b72..8711689120f3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/srq.c +++ b/drivers/net/ethernet/mellanox/mlx4/srq.c @@ -44,13 +44,14 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type) { struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table; struct mlx4_srq *srq; + unsigned long flags; - rcu_read_lock(); + spin_lock_irqsave(&srq_table->lock, flags); srq = radix_tree_lookup(&srq_table->tree, srqn & (dev->caps.num_srqs - 1)); - rcu_read_unlock(); - if (srq) - refcount_inc(&srq->refcount); - else { + if (!srq || !refcount_inc_not_zero(&srq->refcount)) + srq = NULL; + spin_unlock_irqrestore(&srq_table->lock, flags); + if (!srq) { mlx4_warn(dev, "Async event for bogus SRQ %08x\n", srqn); return; } @@ -203,8 +204,8 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd, if (err) goto err_radix; - refcount_set(&srq->refcount, 1); init_completion(&srq->free); + refcount_set_release(&srq->refcount, 1); return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h index c3408b3f7010..091b80a67189 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs.h @@ -201,7 +201,10 @@ int mlx5e_add_vlan_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_nu void mlx5e_remove_vlan_trap(struct mlx5e_flow_steering *fs); int mlx5e_add_mac_trap(struct mlx5e_flow_steering *fs, int trap_id, int tir_num); void mlx5e_remove_mac_trap(struct mlx5e_flow_steering *fs); -void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, struct net_device *netdev); +void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc); int mlx5e_fs_vlan_rx_add_vid(struct mlx5e_flow_steering *fs, struct net_device *netdev, __be16 proto, u16 vid); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c index 6a50b6dec0fa..d9adb993e64d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c @@ -1070,29 +1070,37 @@ static struct psp_dev_ops mlx5_psp_ops = { void mlx5e_psp_unregister(struct mlx5e_priv *priv) { - if (!priv->psp || !priv->psp->psp) + struct mlx5e_psp *psp = priv->psp; + + if (!psp || !psp->psp) return; - psp_dev_unregister(priv->psp->psp); + psp_dev_unregister(psp->psp); + psp->psp = NULL; } void mlx5e_psp_register(struct mlx5e_priv *priv) { + struct mlx5e_psp *psp = priv->psp; + struct psp_dev *psd; + /* FW Caps missing */ if (!priv->psp) return; - priv->psp->caps.assoc_drv_spc = sizeof(u32); - priv->psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128; + psp->caps.assoc_drv_spc = sizeof(u32); + psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128; if (MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_encrypt) && MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_decrypt)) - priv->psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256; + psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256; - priv->psp->psp = psp_dev_create(priv->netdev, &mlx5_psp_ops, - &priv->psp->caps, NULL); - if (IS_ERR(priv->psp->psp)) + psd = psp_dev_create(priv->netdev, &mlx5_psp_ops, &psp->caps, NULL); + if (IS_ERR(psd)) { mlx5_core_err(priv->mdev, "PSP failed to register due to %pe\n", - priv->psp->psp); + psd); + return; + } + psp->psp = psd; } int mlx5e_psp_init(struct mlx5e_priv *priv) @@ -1131,22 +1139,18 @@ int mlx5e_psp_init(struct mlx5e_priv *priv) if (!psp) return -ENOMEM; - priv->psp = psp; fs = mlx5e_accel_psp_fs_init(priv); if (IS_ERR(fs)) { err = PTR_ERR(fs); - goto out_err; + kfree(psp); + return err; } psp->fs = fs; + priv->psp = psp; mlx5_core_dbg(priv->mdev, "PSP attached to netdevice\n"); return 0; - -out_err: - priv->psp = NULL; - kfree(psp); - return err; } void mlx5e_psp_cleanup(struct mlx5e_priv *priv) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index fdfe9d1cfe21..12492c4a5d41 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -609,20 +609,26 @@ static void mlx5e_execute_l2_action(struct mlx5e_flow_steering *fs, } static void mlx5e_sync_netdev_addr(struct mlx5e_flow_steering *fs, - struct net_device *netdev) + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct netdev_hw_addr *ha; - netif_addr_lock_bh(netdev); + if (!uc || !mc) { + netif_addr_lock_bh(netdev); + mlx5e_sync_netdev_addr(fs, netdev, &netdev->uc, &netdev->mc); + netif_addr_unlock_bh(netdev); + return; + } mlx5e_add_l2_to_hash(fs->l2.netdev_uc, netdev->dev_addr); - netdev_for_each_uc_addr(ha, netdev) + + netdev_hw_addr_list_for_each(ha, uc) mlx5e_add_l2_to_hash(fs->l2.netdev_uc, ha->addr); - netdev_for_each_mc_addr(ha, netdev) + netdev_hw_addr_list_for_each(ha, mc) mlx5e_add_l2_to_hash(fs->l2.netdev_mc, ha->addr); - - netif_addr_unlock_bh(netdev); } static void mlx5e_fill_addr_array(struct mlx5e_flow_steering *fs, int list_type, @@ -724,7 +730,9 @@ static void mlx5e_apply_netdev_addr(struct mlx5e_flow_steering *fs) } static void mlx5e_handle_netdev_addr(struct mlx5e_flow_steering *fs, - struct net_device *netdev) + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct mlx5e_l2_hash_node *hn; struct hlist_node *tmp; @@ -736,7 +744,7 @@ static void mlx5e_handle_netdev_addr(struct mlx5e_flow_steering *fs, hn->action = MLX5E_ACTION_DEL; if (fs->state_destroy) - mlx5e_sync_netdev_addr(fs, netdev); + mlx5e_sync_netdev_addr(fs, netdev, uc, mc); mlx5e_apply_netdev_addr(fs); } @@ -820,13 +828,15 @@ static void mlx5e_destroy_promisc_table(struct mlx5e_flow_steering *fs) } void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, - struct net_device *netdev) + struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5e_l2_table *ea = &fs->l2; if (mlx5e_is_uplink_rep(priv)) { - mlx5e_handle_netdev_addr(fs, netdev); + mlx5e_handle_netdev_addr(fs, netdev, uc, mc); goto update_vport_context; } @@ -856,7 +866,7 @@ void mlx5e_fs_set_rx_mode_work(struct mlx5e_flow_steering *fs, if (enable_broadcast) mlx5e_add_l2_flow_rule(fs, &ea->broadcast, MLX5E_FULLMATCH); - mlx5e_handle_netdev_addr(fs, netdev); + mlx5e_handle_netdev_addr(fs, netdev, uc, mc); if (disable_broadcast) mlx5e_del_l2_flow_rule(fs, &ea->broadcast); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 6c4eeb88588c..8f2b3abe0092 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4145,11 +4145,13 @@ static void mlx5e_nic_set_rx_mode(struct mlx5e_priv *priv) queue_work(priv->wq, &priv->set_rx_mode_work); } -static void mlx5e_set_rx_mode(struct net_device *dev) +static void mlx5e_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct mlx5e_priv *priv = netdev_priv(dev); - mlx5e_nic_set_rx_mode(priv); + mlx5e_fs_set_rx_mode_work(priv->fs, dev, uc, mc); } static int mlx5e_set_mac(struct net_device *netdev, void *addr) @@ -5324,7 +5326,7 @@ const struct net_device_ops mlx5e_netdev_ops = { .ndo_setup_tc = mlx5e_setup_tc, .ndo_select_queue = mlx5e_select_queue, .ndo_get_stats64 = mlx5e_get_stats, - .ndo_set_rx_mode = mlx5e_set_rx_mode, + .ndo_set_rx_mode_async = mlx5e_set_rx_mode, .ndo_set_mac_address = mlx5e_set_mac, .ndo_vlan_rx_add_vid = mlx5e_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid, @@ -6021,7 +6023,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev, if (take_rtnl) rtnl_lock(); - mlx5e_psp_register(priv); /* update XDP supported features */ mlx5e_set_xdp_feature(priv); @@ -6034,7 +6035,6 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev, static void mlx5e_nic_cleanup(struct mlx5e_priv *priv) { mlx5e_health_destroy_reporters(priv); - mlx5e_psp_unregister(priv); mlx5e_ktls_cleanup(priv); mlx5e_psp_cleanup(priv); mlx5e_fs_cleanup(priv->fs); @@ -6158,6 +6158,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5e_fs_init_l2_addr(priv->fs, netdev); mlx5e_ipsec_init(priv); + mlx5e_psp_register(priv); err = mlx5e_macsec_init(priv); if (err) @@ -6228,6 +6229,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) mlx5_lag_remove_netdev(mdev, priv->netdev); mlx5_vxlan_reset_to_default(mdev->vxlan); mlx5e_macsec_cleanup(priv); + mlx5e_psp_unregister(priv); mlx5e_ipsec_cleanup(priv); } @@ -6309,8 +6311,11 @@ void mlx5e_set_rx_mode_work(struct work_struct *work) { struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv, set_rx_mode_work); + struct net_device *dev = priv->netdev; - return mlx5e_fs_set_rx_mode_work(priv->fs, priv->netdev); + netdev_lock_ops(dev); + mlx5e_fs_set_rx_mode_work(priv->fs, dev, NULL, NULL); + netdev_unlock_ops(dev); } /* mlx5e generic netdev management API (move to en_common.c) */ @@ -6769,9 +6774,11 @@ static int mlx5e_resume(struct auxiliary_device *adev) return err; actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx); - if (actual_adev) - return _mlx5e_resume(actual_adev); - return 0; + if (actual_adev) { + err = _mlx5e_resume(actual_adev); + mlx5_sd_put_adev(actual_adev, adev); + } + return err; } static int _mlx5e_suspend(struct auxiliary_device *adev, bool pre_netdev_reg) @@ -6810,6 +6817,8 @@ static int mlx5e_suspend(struct auxiliary_device *adev, pm_message_t state) err = _mlx5e_suspend(actual_adev, false); mlx5_sd_cleanup(mdev); + if (actual_adev) + mlx5_sd_put_adev(actual_adev, adev); return err; } @@ -6907,9 +6916,19 @@ static int mlx5e_probe(struct auxiliary_device *adev, return err; actual_adev = mlx5_sd_get_adev(mdev, adev, edev->idx); - if (actual_adev) - return _mlx5e_probe(actual_adev); + if (actual_adev) { + err = _mlx5e_probe(actual_adev); + if (err) + goto sd_cleanup; + mlx5_sd_put_adev(actual_adev, adev); + } return 0; + +sd_cleanup: + mlx5_sd_cleanup(mdev); + if (actual_adev) + mlx5_sd_put_adev(actual_adev, adev); + return err; } static void _mlx5e_remove(struct auxiliary_device *adev) @@ -6961,6 +6980,8 @@ static void mlx5e_remove(struct auxiliary_device *adev) _mlx5e_remove(actual_adev); mlx5_sd_cleanup(mdev); + if (actual_adev) + mlx5_sd_put_adev(actual_adev, adev); } static const struct auxiliary_device_id mlx5e_id_table[] = { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c index 762c783156b4..6e199161b008 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c @@ -18,6 +18,7 @@ struct mlx5_sd { u8 host_buses; struct mlx5_devcom_comp_dev *devcom; struct dentry *dfs; + u8 state; bool primary; union { struct { /* primary */ @@ -31,6 +32,11 @@ struct mlx5_sd { }; }; +enum mlx5_sd_state { + MLX5_SD_STATE_DOWN = 0, + MLX5_SD_STATE_UP, +}; + static int mlx5_sd_get_host_buses(struct mlx5_core_dev *dev) { struct mlx5_sd *sd = mlx5_get_sd(dev); @@ -270,9 +276,6 @@ static void sd_unregister(struct mlx5_core_dev *dev) { struct mlx5_sd *sd = mlx5_get_sd(dev); - mlx5_devcom_comp_lock(sd->devcom); - mlx5_devcom_comp_set_ready(sd->devcom, false); - mlx5_devcom_comp_unlock(sd->devcom); mlx5_devcom_unregister_component(sd->devcom); } @@ -426,6 +429,7 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) struct mlx5_core_dev *primary, *pos, *to; struct mlx5_sd *sd = mlx5_get_sd(dev); u8 alias_key[ACCESS_KEY_LEN]; + struct mlx5_sd *primary_sd; int err, i; err = sd_init(dev); @@ -440,10 +444,17 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) if (err) goto err_sd_cleanup; + mlx5_devcom_comp_lock(sd->devcom); if (!mlx5_devcom_comp_is_ready(sd->devcom)) - return 0; + goto out; primary = mlx5_sd_get_primary(dev); + if (!primary) + goto out; + + primary_sd = mlx5_get_sd(primary); + if (primary_sd->state != MLX5_SD_STATE_DOWN) + goto out; for (i = 0; i < ACCESS_KEY_LEN; i++) alias_key[i] = get_random_u8(); @@ -452,9 +463,13 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) if (err) goto err_sd_unregister; - sd->dfs = debugfs_create_dir("multi-pf", mlx5_debugfs_get_dev_root(primary)); - debugfs_create_x32("group_id", 0400, sd->dfs, &sd->group_id); - debugfs_create_file("primary", 0400, sd->dfs, primary, &dev_fops); + primary_sd->dfs = + debugfs_create_dir("multi-pf", + mlx5_debugfs_get_dev_root(primary)); + debugfs_create_x32("group_id", 0400, primary_sd->dfs, + &primary_sd->group_id); + debugfs_create_file("primary", 0400, primary_sd->dfs, primary, + &dev_fops); mlx5_sd_for_each_secondary(i, primary, pos) { char name[32]; @@ -464,7 +479,8 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) goto err_unset_secondaries; snprintf(name, sizeof(name), "secondary_%d", i - 1); - debugfs_create_file(name, 0400, sd->dfs, pos, &dev_fops); + debugfs_create_file(name, 0400, primary_sd->dfs, pos, + &dev_fops); } @@ -472,6 +488,9 @@ int mlx5_sd_init(struct mlx5_core_dev *dev) sd->group_id, mlx5_devcom_comp_get_size(sd->devcom)); sd_print_group(primary); + primary_sd->state = MLX5_SD_STATE_UP; +out: + mlx5_devcom_comp_unlock(sd->devcom); return 0; err_unset_secondaries: @@ -479,8 +498,18 @@ err_unset_secondaries: mlx5_sd_for_each_secondary_to(i, primary, to, pos) sd_cmd_unset_secondary(pos); sd_cmd_unset_primary(primary); - debugfs_remove_recursive(sd->dfs); + debugfs_remove_recursive(primary_sd->dfs); + primary_sd->dfs = NULL; err_sd_unregister: + mlx5_sd_for_each_secondary(i, primary, pos) { + struct mlx5_sd *peer_sd = mlx5_get_sd(pos); + + primary_sd->secondaries[i - 1] = NULL; + peer_sd->primary_dev = NULL; + } + primary_sd->primary = false; + mlx5_devcom_comp_set_ready(sd->devcom, false); + mlx5_devcom_comp_unlock(sd->devcom); sd_unregister(dev); err_sd_cleanup: sd_cleanup(dev); @@ -491,42 +520,97 @@ void mlx5_sd_cleanup(struct mlx5_core_dev *dev) { struct mlx5_sd *sd = mlx5_get_sd(dev); struct mlx5_core_dev *primary, *pos; + struct mlx5_sd *primary_sd; int i; if (!sd) return; + mlx5_devcom_comp_lock(sd->devcom); if (!mlx5_devcom_comp_is_ready(sd->devcom)) - goto out; + goto out_unlock; primary = mlx5_sd_get_primary(dev); + if (!primary) + goto out_ready_false; + + primary_sd = mlx5_get_sd(primary); + if (primary_sd->state != MLX5_SD_STATE_UP) + goto out_clear_peers; + mlx5_sd_for_each_secondary(i, primary, pos) sd_cmd_unset_secondary(pos); sd_cmd_unset_primary(primary); - debugfs_remove_recursive(sd->dfs); + debugfs_remove_recursive(primary_sd->dfs); + primary_sd->dfs = NULL; sd_info(primary, "group id %#x, uncombined\n", sd->group_id); -out: + primary_sd->state = MLX5_SD_STATE_DOWN; +out_clear_peers: + mlx5_sd_for_each_secondary(i, primary, pos) { + struct mlx5_sd *peer_sd = mlx5_get_sd(pos); + + primary_sd->secondaries[i - 1] = NULL; + peer_sd->primary_dev = NULL; + } + primary_sd->primary = false; +out_ready_false: + mlx5_devcom_comp_set_ready(sd->devcom, false); +out_unlock: + mlx5_devcom_comp_unlock(sd->devcom); sd_unregister(dev); sd_cleanup(dev); } +/* Lock order: + * primary: actual_adev_lock -> SD devcom comp lock + * secondary: SD devcom comp lock -> (drop) -> actual_adev_lock + * The two locks are never held together, so no ABBA. + */ struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev, struct auxiliary_device *adev, int idx) { struct mlx5_sd *sd = mlx5_get_sd(dev); struct mlx5_core_dev *primary; + struct mlx5_adev *primary_adev; if (!sd) return adev; - if (!mlx5_devcom_comp_is_ready(sd->devcom)) + mlx5_devcom_comp_lock(sd->devcom); + if (!mlx5_devcom_comp_is_ready(sd->devcom)) { + mlx5_devcom_comp_unlock(sd->devcom); return NULL; + } primary = mlx5_sd_get_primary(dev); - if (dev == primary) + if (!primary || dev == primary) { + mlx5_devcom_comp_unlock(sd->devcom); return adev; + } + + primary_adev = primary->priv.adev[idx]; + get_device(&primary_adev->adev.dev); + mlx5_devcom_comp_unlock(sd->devcom); + + device_lock(&primary_adev->adev.dev); + /* Primary may have completed remove between dropping devcom and + * acquiring device_lock; recheck. + */ + if (!mlx5_devcom_comp_is_ready(sd->devcom)) { + device_unlock(&primary_adev->adev.dev); + put_device(&primary_adev->adev.dev); + return NULL; + } + return &primary_adev->adev; +} - return &primary->priv.adev[idx]->adev; +void mlx5_sd_put_adev(struct auxiliary_device *actual_adev, + struct auxiliary_device *adev) +{ + if (actual_adev != adev) { + device_unlock(&actual_adev->dev); + put_device(&actual_adev->dev); + } } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h index 137efaf9aabc..9bfd5b9756b5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.h @@ -15,6 +15,8 @@ struct mlx5_core_dev *mlx5_sd_ch_ix_get_dev(struct mlx5_core_dev *primary, int c struct auxiliary_device *mlx5_sd_get_adev(struct mlx5_core_dev *dev, struct auxiliary_device *adev, int idx); +void mlx5_sd_put_adev(struct auxiliary_device *actual_adev, + struct auxiliary_device *adev); int mlx5_sd_init(struct mlx5_core_dev *dev); void mlx5_sd_cleanup(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 8761b9deed1c..74827e8ca125 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -111,74 +111,9 @@ static struct mlx5_profile profile[] = { }, [2] = { - .mask = MLX5_PROF_MASK_QP_SIZE | - MLX5_PROF_MASK_MR_CACHE, + .mask = MLX5_PROF_MASK_QP_SIZE, .log_max_qp = LOG_MAX_SUPPORTED_QPS, .num_cmd_caches = MLX5_NUM_COMMAND_CACHES, - .mr_cache[0] = { - .size = 500, - .limit = 250 - }, - .mr_cache[1] = { - .size = 500, - .limit = 250 - }, - .mr_cache[2] = { - .size = 500, - .limit = 250 - }, - .mr_cache[3] = { - .size = 500, - .limit = 250 - }, - .mr_cache[4] = { - .size = 500, - .limit = 250 - }, - .mr_cache[5] = { - .size = 500, - .limit = 250 - }, - .mr_cache[6] = { - .size = 500, - .limit = 250 - }, - .mr_cache[7] = { - .size = 500, - .limit = 250 - }, - .mr_cache[8] = { - .size = 500, - .limit = 250 - }, - .mr_cache[9] = { - .size = 500, - .limit = 250 - }, - .mr_cache[10] = { - .size = 500, - .limit = 250 - }, - .mr_cache[11] = { - .size = 500, - .limit = 250 - }, - .mr_cache[12] = { - .size = 64, - .limit = 32 - }, - .mr_cache[13] = { - .size = 32, - .limit = 16 - }, - .mr_cache[14] = { - .size = 16, - .limit = 8 - }, - .mr_cache[15] = { - .size = 8, - .limit = 4 - }, }, [3] = { .mask = MLX5_PROF_MASK_QP_SIZE, @@ -1914,7 +1849,7 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) err = mlx5_notifiers_init(dev); if (err) - goto err_hca_caps; + goto err_notifiers_init; /* The conjunction of sw_vhca_id with sw_owner_id will be a global * unique id per function which uses mlx5_core. @@ -1930,6 +1865,8 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) return 0; +err_notifiers_init: + mlx5_hca_caps_free(dev); err_hca_caps: mlx5_adev_cleanup(dev); err_adev_init: diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c index b4b396ca9bce..4dea2bb58d2f 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c @@ -183,7 +183,9 @@ static int fbnic_mc_unsync(struct net_device *netdev, const unsigned char *addr) return ret; } -void __fbnic_set_rx_mode(struct fbnic_dev *fbd) +void __fbnic_set_rx_mode(struct fbnic_dev *fbd, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { bool uc_promisc = false, mc_promisc = false; struct net_device *netdev = fbd->netdev; @@ -213,10 +215,10 @@ void __fbnic_set_rx_mode(struct fbnic_dev *fbd) } /* Synchronize unicast and multicast address lists */ - err = __dev_uc_sync(netdev, fbnic_uc_sync, fbnic_uc_unsync); + err = __hw_addr_sync_dev(uc, netdev, fbnic_uc_sync, fbnic_uc_unsync); if (err == -ENOSPC) uc_promisc = true; - err = __dev_mc_sync(netdev, fbnic_mc_sync, fbnic_mc_unsync); + err = __hw_addr_sync_dev(mc, netdev, fbnic_mc_sync, fbnic_mc_unsync); if (err == -ENOSPC) mc_promisc = true; @@ -238,18 +240,21 @@ void __fbnic_set_rx_mode(struct fbnic_dev *fbd) fbnic_write_tce_tcam(fbd); } -static void fbnic_set_rx_mode(struct net_device *netdev) +static void fbnic_set_rx_mode(struct net_device *netdev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { struct fbnic_net *fbn = netdev_priv(netdev); struct fbnic_dev *fbd = fbn->fbd; /* No need to update the hardware if we are not running */ if (netif_running(netdev)) - __fbnic_set_rx_mode(fbd); + __fbnic_set_rx_mode(fbd, uc, mc); } static int fbnic_set_mac(struct net_device *netdev, void *p) { + struct fbnic_net *fbn = netdev_priv(netdev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) @@ -257,7 +262,8 @@ static int fbnic_set_mac(struct net_device *netdev, void *p) eth_hw_addr_set(netdev, addr->sa_data); - fbnic_set_rx_mode(netdev); + if (netif_running(netdev)) + __fbnic_set_rx_mode(fbn->fbd, &netdev->uc, &netdev->mc); return 0; } @@ -551,7 +557,7 @@ static const struct net_device_ops fbnic_netdev_ops = { .ndo_features_check = fbnic_features_check, .ndo_set_mac_address = fbnic_set_mac, .ndo_change_mtu = fbnic_change_mtu, - .ndo_set_rx_mode = fbnic_set_rx_mode, + .ndo_set_rx_mode_async = fbnic_set_rx_mode, .ndo_get_stats64 = fbnic_get_stats64, .ndo_bpf = fbnic_bpf, .ndo_hwtstamp_get = fbnic_hwtstamp_get, @@ -820,7 +826,8 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd) netif_tx_stop_all_queues(netdev); if (fbnic_phylink_create(netdev)) { - fbnic_netdev_free(fbd); + free_netdev(netdev); + fbd->netdev = NULL; return NULL; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h index 9129a658f8fa..eded20b0e9e4 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h +++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h @@ -97,7 +97,9 @@ void fbnic_time_init(struct fbnic_net *fbn); int fbnic_time_start(struct fbnic_net *fbn); void fbnic_time_stop(struct fbnic_net *fbn); -void __fbnic_set_rx_mode(struct fbnic_dev *fbd); +void __fbnic_set_rx_mode(struct fbnic_dev *fbd, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc); void fbnic_clear_rx_mode(struct fbnic_dev *fbd); void fbnic_phylink_get_pauseparam(struct net_device *netdev, diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c index b7c0b7349d00..7e85b480203c 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c @@ -135,7 +135,7 @@ void fbnic_up(struct fbnic_net *fbn) fbnic_rss_reinit_hw(fbn->fbd, fbn); - __fbnic_set_rx_mode(fbn->fbd); + __fbnic_set_rx_mode(fbn->fbd, &fbn->netdev->uc, &fbn->netdev->mc); /* Enable Tx/Rx processing */ fbnic_napi_enable(fbn); @@ -180,7 +180,7 @@ static int fbnic_fw_config_after_crash(struct fbnic_dev *fbd) } fbnic_rpc_reset_valid_entries(fbd); - __fbnic_set_rx_mode(fbd); + __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); return 0; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c index 42a186db43ea..fe95b6f69646 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_rpc.c @@ -244,7 +244,7 @@ void fbnic_bmc_rpc_check(struct fbnic_dev *fbd) if (fbd->fw_cap.need_bmc_tcam_reinit) { fbnic_bmc_rpc_init(fbd); - __fbnic_set_rx_mode(fbd); + __fbnic_set_rx_mode(fbd, &fbd->netdev->uc, &fbd->netdev->mc); fbd->fw_cap.need_bmc_tcam_reinit = false; } diff --git a/drivers/net/ethernet/micrel/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h index 31f75b4a67fd..b795a3a60571 100644 --- a/drivers/net/ethernet/micrel/ks8851.h +++ b/drivers/net/ethernet/micrel/ks8851.h @@ -408,10 +408,8 @@ struct ks8851_net { struct gpio_desc *gpio; struct mii_bus *mii_bus; - void (*lock)(struct ks8851_net *ks, - unsigned long *flags); - void (*unlock)(struct ks8851_net *ks, - unsigned long *flags); + void (*lock)(struct ks8851_net *ks); + void (*unlock)(struct ks8851_net *ks); unsigned int (*rdreg16)(struct ks8851_net *ks, unsigned int reg); void (*wrreg16)(struct ks8851_net *ks, diff --git a/drivers/net/ethernet/micrel/ks8851_common.c b/drivers/net/ethernet/micrel/ks8851_common.c index 8048770958d6..4afbb40bc0e4 100644 --- a/drivers/net/ethernet/micrel/ks8851_common.c +++ b/drivers/net/ethernet/micrel/ks8851_common.c @@ -28,25 +28,23 @@ /** * ks8851_lock - register access lock * @ks: The chip state - * @flags: Spinlock flags * * Claim chip register access lock */ -static void ks8851_lock(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_lock(struct ks8851_net *ks) { - ks->lock(ks, flags); + ks->lock(ks); } /** * ks8851_unlock - register access unlock * @ks: The chip state - * @flags: Spinlock flags * * Release chip register access lock */ -static void ks8851_unlock(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_unlock(struct ks8851_net *ks) { - ks->unlock(ks, flags); + ks->unlock(ks); } /** @@ -129,11 +127,10 @@ static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode) static int ks8851_write_mac_addr(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; u16 val; int i; - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* * Wake up chip in case it was powered off when stopped; otherwise, @@ -149,7 +146,7 @@ static int ks8851_write_mac_addr(struct net_device *dev) if (!netif_running(dev)) ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return 0; } @@ -163,12 +160,11 @@ static int ks8851_write_mac_addr(struct net_device *dev) static void ks8851_read_mac_addr(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; u8 addr[ETH_ALEN]; u16 reg; int i; - ks8851_lock(ks, &flags); + ks8851_lock(ks); for (i = 0; i < ETH_ALEN; i += 2) { reg = ks8851_rdreg16(ks, KS_MAR(i)); @@ -177,7 +173,7 @@ static void ks8851_read_mac_addr(struct net_device *dev) } eth_hw_addr_set(dev, addr); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); } /** @@ -312,11 +308,10 @@ static irqreturn_t ks8851_irq(int irq, void *_ks) { struct ks8851_net *ks = _ks; struct sk_buff_head rxq; - unsigned long flags; unsigned int status; struct sk_buff *skb; - ks8851_lock(ks, &flags); + ks8851_lock(ks); status = ks8851_rdreg16(ks, KS_ISR); ks8851_wrreg16(ks, KS_ISR, status); @@ -373,14 +368,17 @@ static irqreturn_t ks8851_irq(int irq, void *_ks) ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1); } - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); if (status & IRQ_LCI) mii_check_link(&ks->mii); - if (status & IRQ_RXI) + if (status & IRQ_RXI) { + local_bh_disable(); while ((skb = __skb_dequeue(&rxq))) netif_rx(skb); + local_bh_enable(); + } return IRQ_HANDLED; } @@ -405,7 +403,6 @@ static void ks8851_flush_tx_work(struct ks8851_net *ks) static int ks8851_net_open(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; int ret; ret = request_threaded_irq(dev->irq, NULL, ks8851_irq, @@ -418,7 +415,7 @@ static int ks8851_net_open(struct net_device *dev) /* lock the card, even if we may not actually be doing anything * else at the moment */ - ks8851_lock(ks, &flags); + ks8851_lock(ks); netif_dbg(ks, ifup, ks->netdev, "opening\n"); @@ -471,7 +468,7 @@ static int ks8851_net_open(struct net_device *dev) netif_dbg(ks, ifup, ks->netdev, "network device up\n"); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); mii_check_link(&ks->mii); return 0; } @@ -487,23 +484,22 @@ static int ks8851_net_open(struct net_device *dev) static int ks8851_net_stop(struct net_device *dev) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; netif_info(ks, ifdown, dev, "shutting down\n"); netif_stop_queue(dev); - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* turn off the IRQs and ack any outstanding */ ks8851_wrreg16(ks, KS_IER, 0x0000); ks8851_wrreg16(ks, KS_ISR, 0xffff); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); /* stop any outstanding work */ ks8851_flush_tx_work(ks); flush_work(&ks->rxctrl_work); - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* shutdown RX process */ ks8851_wrreg16(ks, KS_RXCR1, 0x0000); @@ -512,7 +508,7 @@ static int ks8851_net_stop(struct net_device *dev) /* set powermode to soft power down to save power */ ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); /* ensure any queued tx buffers are dumped */ while (!skb_queue_empty(&ks->txq)) { @@ -566,14 +562,13 @@ static netdev_tx_t ks8851_start_xmit(struct sk_buff *skb, static void ks8851_rxctrl_work(struct work_struct *work) { struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work); - unsigned long flags; - ks8851_lock(ks, &flags); + ks8851_lock(ks); /* need to shutdown RXQ before modifying filter parameters */ ks8851_wrreg16(ks, KS_RXCR1, 0x00); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); } static void ks8851_set_rx_mode(struct net_device *dev) @@ -780,7 +775,6 @@ static int ks8851_set_eeprom(struct net_device *dev, { struct ks8851_net *ks = netdev_priv(dev); int offset = ee->offset; - unsigned long flags; int len = ee->len; u16 tmp; @@ -794,7 +788,7 @@ static int ks8851_set_eeprom(struct net_device *dev, if (!(ks->rc_ccr & CCR_EEPROM)) return -ENOENT; - ks8851_lock(ks, &flags); + ks8851_lock(ks); ks8851_eeprom_claim(ks); @@ -817,7 +811,7 @@ static int ks8851_set_eeprom(struct net_device *dev, eeprom_93cx6_wren(&ks->eeprom, false); ks8851_eeprom_release(ks); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return 0; } @@ -827,7 +821,6 @@ static int ks8851_get_eeprom(struct net_device *dev, { struct ks8851_net *ks = netdev_priv(dev); int offset = ee->offset; - unsigned long flags; int len = ee->len; /* must be 2 byte aligned */ @@ -837,7 +830,7 @@ static int ks8851_get_eeprom(struct net_device *dev, if (!(ks->rc_ccr & CCR_EEPROM)) return -ENOENT; - ks8851_lock(ks, &flags); + ks8851_lock(ks); ks8851_eeprom_claim(ks); @@ -845,7 +838,7 @@ static int ks8851_get_eeprom(struct net_device *dev, eeprom_93cx6_multiread(&ks->eeprom, offset/2, (__le16 *)data, len/2); ks8851_eeprom_release(ks); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return 0; } @@ -904,7 +897,6 @@ static int ks8851_phy_reg(int reg) static int ks8851_phy_read_common(struct net_device *dev, int phy_addr, int reg) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; int result; int ksreg; @@ -912,9 +904,9 @@ static int ks8851_phy_read_common(struct net_device *dev, int phy_addr, int reg) if (ksreg < 0) return ksreg; - ks8851_lock(ks, &flags); + ks8851_lock(ks); result = ks8851_rdreg16(ks, ksreg); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); return result; } @@ -949,14 +941,13 @@ static void ks8851_phy_write(struct net_device *dev, int phy, int reg, int value) { struct ks8851_net *ks = netdev_priv(dev); - unsigned long flags; int ksreg; ksreg = ks8851_phy_reg(reg); if (ksreg >= 0) { - ks8851_lock(ks, &flags); + ks8851_lock(ks); ks8851_wrreg16(ks, ksreg, value); - ks8851_unlock(ks, &flags); + ks8851_unlock(ks); } } diff --git a/drivers/net/ethernet/micrel/ks8851_par.c b/drivers/net/ethernet/micrel/ks8851_par.c index 78695be2570b..9f1c33f6ddec 100644 --- a/drivers/net/ethernet/micrel/ks8851_par.c +++ b/drivers/net/ethernet/micrel/ks8851_par.c @@ -55,29 +55,27 @@ struct ks8851_net_par { /** * ks8851_lock_par - register access lock * @ks: The chip state - * @flags: Spinlock flags * * Claim chip register access lock */ -static void ks8851_lock_par(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_lock_par(struct ks8851_net *ks) { struct ks8851_net_par *ksp = to_ks8851_par(ks); - spin_lock_irqsave(&ksp->lock, *flags); + spin_lock_bh(&ksp->lock); } /** * ks8851_unlock_par - register access unlock * @ks: The chip state - * @flags: Spinlock flags * * Release chip register access lock */ -static void ks8851_unlock_par(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_unlock_par(struct ks8851_net *ks) { struct ks8851_net_par *ksp = to_ks8851_par(ks); - spin_unlock_irqrestore(&ksp->lock, *flags); + spin_unlock_bh(&ksp->lock); } /** @@ -233,7 +231,6 @@ static netdev_tx_t ks8851_start_xmit_par(struct sk_buff *skb, { struct ks8851_net *ks = netdev_priv(dev); netdev_tx_t ret = NETDEV_TX_OK; - unsigned long flags; unsigned int txqcr; u16 txmir; int err; @@ -241,7 +238,7 @@ static netdev_tx_t ks8851_start_xmit_par(struct sk_buff *skb, netif_dbg(ks, tx_queued, ks->netdev, "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data); - ks8851_lock_par(ks, &flags); + ks8851_lock_par(ks); txmir = ks8851_rdreg16_par(ks, KS_TXMIR) & 0x1fff; @@ -262,7 +259,7 @@ static netdev_tx_t ks8851_start_xmit_par(struct sk_buff *skb, ret = NETDEV_TX_BUSY; } - ks8851_unlock_par(ks, &flags); + ks8851_unlock_par(ks); return ret; } diff --git a/drivers/net/ethernet/micrel/ks8851_spi.c b/drivers/net/ethernet/micrel/ks8851_spi.c index a161ae45743a..b9e68520278d 100644 --- a/drivers/net/ethernet/micrel/ks8851_spi.c +++ b/drivers/net/ethernet/micrel/ks8851_spi.c @@ -71,11 +71,10 @@ struct ks8851_net_spi { /** * ks8851_lock_spi - register access lock * @ks: The chip state - * @flags: Spinlock flags * * Claim chip register access lock */ -static void ks8851_lock_spi(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_lock_spi(struct ks8851_net *ks) { struct ks8851_net_spi *kss = to_ks8851_spi(ks); @@ -85,11 +84,10 @@ static void ks8851_lock_spi(struct ks8851_net *ks, unsigned long *flags) /** * ks8851_unlock_spi - register access unlock * @ks: The chip state - * @flags: Spinlock flags * * Release chip register access lock */ -static void ks8851_unlock_spi(struct ks8851_net *ks, unsigned long *flags) +static void ks8851_unlock_spi(struct ks8851_net *ks) { struct ks8851_net_spi *kss = to_ks8851_spi(ks); @@ -309,7 +307,6 @@ static void ks8851_tx_work(struct work_struct *work) struct ks8851_net_spi *kss; unsigned short tx_space; struct ks8851_net *ks; - unsigned long flags; struct sk_buff *txb; bool last; @@ -317,7 +314,7 @@ static void ks8851_tx_work(struct work_struct *work) ks = &kss->ks8851; last = skb_queue_empty(&ks->txq); - ks8851_lock_spi(ks, &flags); + ks8851_lock_spi(ks); while (!last) { txb = skb_dequeue(&ks->txq); @@ -343,7 +340,7 @@ static void ks8851_tx_work(struct work_struct *work) ks->tx_space = tx_space; spin_unlock_bh(&ks->statelock); - ks8851_unlock_spi(ks, &flags); + ks8851_unlock_spi(ks); } /** diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 47752d3fde0b..1179a6e127c5 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) for (p = 0; p < lan966x->num_phys_ports; p++) { port = lan966x->ports[p]; - if (!port) + if (!port || !port->dev) continue; - if (port->dev) - unregister_netdev(port->dev); + unregister_netdev(port->dev); lan966x_xdp_port_deinit(port); if (lan966x->fdma && lan966x->fdma_ndev == port->dev) @@ -873,6 +872,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, err = register_netdev(dev); if (err) { dev_err(lan966x->dev, "register_netdev failed\n"); + phylink_destroy(phylink); + port->phylink = NULL; + port->dev = NULL; return err; } diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h index 6a745bb71b5c..eb57b86fbe22 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h @@ -31,11 +31,11 @@ enum spx5_target_chiptype { SPX5_TARGET_CT_7552 = 0x7552, /* SparX-5-128 Enterprise */ SPX5_TARGET_CT_7556 = 0x7556, /* SparX-5-160 Enterprise */ SPX5_TARGET_CT_7558 = 0x7558, /* SparX-5-200 Enterprise */ - SPX5_TARGET_CT_7546TSN = 0x47546, /* SparX-5-64i Industrial */ - SPX5_TARGET_CT_7549TSN = 0x47549, /* SparX-5-90i Industrial */ - SPX5_TARGET_CT_7552TSN = 0x47552, /* SparX-5-128i Industrial */ - SPX5_TARGET_CT_7556TSN = 0x47556, /* SparX-5-160i Industrial */ - SPX5_TARGET_CT_7558TSN = 0x47558, /* SparX-5-200i Industrial */ + SPX5_TARGET_CT_7546TSN = 0x0546, /* SparX-5-64i Industrial */ + SPX5_TARGET_CT_7549TSN = 0x0549, /* SparX-5-90i Industrial */ + SPX5_TARGET_CT_7552TSN = 0x0552, /* SparX-5-128i Industrial */ + SPX5_TARGET_CT_7556TSN = 0x0556, /* SparX-5-160i Industrial */ + SPX5_TARGET_CT_7558TSN = 0x0558, /* SparX-5-200i Industrial */ SPX5_TARGET_CT_LAN9694 = 0x9694, /* lan969x-40 */ SPX5_TARGET_CT_LAN9691VAO = 0x9691, /* lan969x-40-VAO */ SPX5_TARGET_CT_LAN9694TSN = 0x9695, /* lan969x-40-TSN */ diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c index 04bc8fffaf96..62c49893de3c 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_port.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_port.c @@ -1128,7 +1128,8 @@ int sparx5_port_init(struct sparx5 *sparx5, DEV2G5_PCS1G_SD_CFG(port->portno)); if (conf->portmode == PHY_INTERFACE_MODE_QSGMII || - conf->portmode == PHY_INTERFACE_MODE_SGMII) { + conf->portmode == PHY_INTERFACE_MODE_SGMII || + conf->portmode == PHY_INTERFACE_MODE_1000BASEX) { err = sparx5_serdes_set(sparx5, port, conf); if (err) return err; diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c index 098fbda0d128..d8e816882f02 100644 --- a/drivers/net/ethernet/microsoft/mana/gdma_main.c +++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c @@ -43,8 +43,9 @@ static u64 mana_gd_r64(struct gdma_context *g, u64 offset) static int mana_gd_init_pf_regs(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); - void __iomem *sriov_base_va; + u64 remaining_barsize; u64 sriov_base_off; + u64 sriov_shm_off; gc->db_page_size = mana_gd_r32(gc, GDMA_PF_REG_DB_PAGE_SIZE) & 0xFFFF; @@ -73,10 +74,28 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev) gc->phys_db_page_base = gc->bar0_pa + gc->db_page_off; sriov_base_off = mana_gd_r64(gc, GDMA_SRIOV_REG_CFG_BASE_OFF); + if (sriov_base_off >= gc->bar0_size || + gc->bar0_size - sriov_base_off < + GDMA_PF_REG_SHM_OFF + sizeof(u64) || + !IS_ALIGNED(sriov_base_off, sizeof(u64))) { + dev_err(gc->dev, + "SRIOV base offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n", + sriov_base_off, (u64)gc->bar0_size); + return -EPROTO; + } - sriov_base_va = gc->bar0_va + sriov_base_off; - gc->shm_base = sriov_base_va + - mana_gd_r64(gc, sriov_base_off + GDMA_PF_REG_SHM_OFF); + remaining_barsize = gc->bar0_size - sriov_base_off; + sriov_shm_off = mana_gd_r64(gc, sriov_base_off + GDMA_PF_REG_SHM_OFF); + if (sriov_shm_off >= remaining_barsize || + remaining_barsize - sriov_shm_off < SMC_APERTURE_SIZE || + !IS_ALIGNED(sriov_shm_off, sizeof(u32))) { + dev_err(gc->dev, + "SRIOV SHM offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n", + sriov_shm_off, (u64)gc->bar0_size); + return -EPROTO; + } + + gc->shm_base = gc->bar0_va + sriov_base_off + sriov_shm_off; return 0; } @@ -84,6 +103,7 @@ static int mana_gd_init_pf_regs(struct pci_dev *pdev) static int mana_gd_init_vf_regs(struct pci_dev *pdev) { struct gdma_context *gc = pci_get_drvdata(pdev); + u64 shm_off; gc->db_page_size = mana_gd_r32(gc, GDMA_REG_DB_PAGE_SIZE) & 0xFFFF; @@ -111,7 +131,17 @@ static int mana_gd_init_vf_regs(struct pci_dev *pdev) gc->db_page_base = gc->bar0_va + gc->db_page_off; gc->phys_db_page_base = gc->bar0_pa + gc->db_page_off; - gc->shm_base = gc->bar0_va + mana_gd_r64(gc, GDMA_REG_SHM_OFFSET); + shm_off = mana_gd_r64(gc, GDMA_REG_SHM_OFFSET); + if (shm_off >= gc->bar0_size || + gc->bar0_size - shm_off < SMC_APERTURE_SIZE || + !IS_ALIGNED(shm_off, sizeof(u32))) { + dev_err(gc->dev, + "SHM offset 0x%llx out of range or unaligned (BAR0 size 0x%llx)\n", + shm_off, (u64)gc->bar0_size); + return -EPROTO; + } + + gc->shm_base = gc->bar0_va + shm_off; return 0; } diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 6302432b9bf6..9afc786b297a 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -2520,9 +2520,12 @@ static void mana_destroy_rxq(struct mana_port_context *apc, napi_disable_locked(napi); netif_napi_del_locked(napi); } - xdp_rxq_info_unreg(&rxq->xdp_rxq); - mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj); + if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) + xdp_rxq_info_unreg(&rxq->xdp_rxq); + + if (rxq->rxobj != INVALID_MANA_HANDLE) + mana_destroy_wq_obj(apc, GDMA_RQ, rxq->rxobj); mana_deinit_cq(apc, &rxq->rx_cq); @@ -2796,9 +2799,6 @@ out: mana_destroy_rxq(apc, rxq, false); - if (cq) - mana_deinit_cq(apc, cq); - return NULL; } @@ -2926,6 +2926,13 @@ static void mana_rss_table_init(struct mana_port_context *apc) ethtool_rxfh_indir_default(i, apc->num_queues); } +int mana_disable_vport_rx(struct mana_port_context *apc) +{ + return mana_cfg_vport_steering(apc, TRI_STATE_FALSE, false, false, + false); +} +EXPORT_SYMBOL_NS(mana_disable_vport_rx, "NET_MANA"); + int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx, bool update_hash, bool update_tab) { @@ -3312,10 +3319,12 @@ static int mana_dealloc_queues(struct net_device *ndev) */ apc->rss_state = TRI_STATE_FALSE; - err = mana_config_rss(apc, TRI_STATE_FALSE, false, false); + err = mana_disable_vport_rx(apc); if (err && mana_en_need_log(apc, err)) netdev_err(ndev, "Failed to disable vPort: %d\n", err); + mana_fence_rqs(apc); + /* Even in err case, still need to cleanup the vPort */ mana_destroy_vport(apc); @@ -3631,8 +3640,12 @@ int mana_probe(struct gdma_dev *gd, bool resuming) ac->gdma_dev = gd; gd->driver_data = ac; + + INIT_WORK(&ac->link_change_work, mana_link_state_handle); } + INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler); + err = mana_create_eq(ac); if (err) { dev_err(dev, "Failed to create EQs: %d\n", err); @@ -3648,8 +3661,6 @@ int mana_probe(struct gdma_dev *gd, bool resuming) if (!resuming) { ac->num_ports = num_ports; - - INIT_WORK(&ac->link_change_work, mana_link_state_handle); } else { if (ac->num_ports != num_ports) { dev_err(dev, "The number of vPorts changed: %d->%d\n", @@ -3678,10 +3689,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming) if (!resuming) { for (i = 0; i < ac->num_ports; i++) { err = mana_probe_port(ac, i, &ac->ports[i]); - /* we log the port for which the probe failed and stop - * probes for subsequent ports. - * Note that we keep running ports, for which the probes - * were successful, unless add_adev fails too + /* Log the port for which the probe failed, stop probing + * subsequent ports, and skip add_adev. + * mana_remove() will clean up already-probed ports. */ if (err) { dev_err(dev, "Probe Failed for port %d\n", i); @@ -3695,10 +3705,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming) enable_work(&apc->queue_reset_work); err = mana_attach(ac->ports[i]); rtnl_unlock(); - /* we log the port for which the attach failed and stop - * attach for subsequent ports - * Note that we keep running ports, for which the attach - * were successful, unless add_adev fails too + /* Log the port for which the attach failed, stop + * attaching subsequent ports, and skip add_adev. + * mana_remove() will clean up already-attached ports. */ if (err) { dev_err(dev, "Attach Failed for port %d\n", i); @@ -3707,9 +3716,9 @@ int mana_probe(struct gdma_dev *gd, bool resuming) } } - err = add_adev(gd, "eth"); + if (!err) + err = add_adev(gd, "eth"); - INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler); schedule_delayed_work(&ac->gf_stats_work, MANA_GF_STATS_PERIOD); out: @@ -3730,11 +3739,16 @@ void mana_remove(struct gdma_dev *gd, bool suspending) struct gdma_context *gc = gd->gdma_context; struct mana_context *ac = gd->driver_data; struct mana_port_context *apc; - struct device *dev = gc->dev; + struct device *dev; struct net_device *ndev; int err; int i; + if (!gc || !ac) + return; + + dev = gc->dev; + disable_work_sync(&ac->link_change_work); cancel_delayed_work_sync(&ac->gf_stats_work); @@ -3747,7 +3761,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) if (!ndev) { if (i == 0) dev_err(dev, "No net device to remove\n"); - goto out; + break; } apc = netdev_priv(ndev); @@ -3778,7 +3792,7 @@ void mana_remove(struct gdma_dev *gd, bool suspending) } mana_destroy_eq(ac); -out: + if (ac->per_port_queue_reset_wq) { destroy_workqueue(ac->per_port_queue_reset_wq); ac->per_port_queue_reset_wq = NULL; diff --git a/drivers/net/ethernet/microsoft/mana/shm_channel.c b/drivers/net/ethernet/microsoft/mana/shm_channel.c index 0f1679ebad96..d21b5db06e50 100644 --- a/drivers/net/ethernet/microsoft/mana/shm_channel.c +++ b/drivers/net/ethernet/microsoft/mana/shm_channel.c @@ -61,11 +61,6 @@ union smc_proto_hdr { }; }; /* HW DATA */ -#define SMC_APERTURE_BITS 256 -#define SMC_BASIC_UNIT (sizeof(u32)) -#define SMC_APERTURE_DWORDS (SMC_APERTURE_BITS / (SMC_BASIC_UNIT * 8)) -#define SMC_LAST_DWORD (SMC_APERTURE_DWORDS - 1) - static int mana_smc_poll_register(void __iomem *base, bool reset) { void __iomem *ptr = base + SMC_LAST_DWORD * SMC_BASIC_UNIT; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c index 79470f198a62..9cf19446657c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_target.c @@ -435,12 +435,17 @@ static int nfp_encode_basic_qdr(u64 addr, int dest_island, int cpp_tgt, /* Full Island ID and channel bits overlap? */ ret = nfp_decode_basic(addr, &v, cpp_tgt, mode, addr40, isld1, isld0); - if (ret) + if (ret) { + pr_warn("%s: decode dest_island failed: %d\n", __func__, ret); return ret; + } /* The current address won't go where expected? */ - if (dest_island != -1 && dest_island != v) + if (dest_island != -1 && dest_island != v) { + pr_warn("%s: dest_island mismatch: current (%d) != decoded (%d)\n", + __func__, dest_island, v); return -EINVAL; + } /* If dest_island was -1, we don't care where it goes. */ return 0; @@ -493,7 +498,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, * the address but we can verify if the existing * contents will point to a valid island. */ - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); iid_lsb = addr40 ? 34 : 26; @@ -504,7 +509,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, return 0; case 1: if (cpp_tgt == NFP_CPP_TARGET_QDR && !addr40) - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); idx_lsb = addr40 ? 39 : 31; @@ -530,7 +535,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, * be set before hand and with them select an island. * So we need to confirm that it's at least plausible. */ - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); /* Make sure we compare against isldN values @@ -551,7 +556,7 @@ static int nfp_encode_basic(u64 *addr, int dest_island, int cpp_tgt, * iid<1> = addr<30> = channel<0> * channel<1> = addr<31> = Index */ - return nfp_encode_basic_qdr(*addr, cpp_tgt, dest_island, + return nfp_encode_basic_qdr(*addr, dest_island, cpp_tgt, mode, addr40, isld1, isld0); isld[0] &= ~3; diff --git a/drivers/net/ethernet/packetengines/Kconfig b/drivers/net/ethernet/packetengines/Kconfig deleted file mode 100644 index de91331dcb7d..000000000000 --- a/drivers/net/ethernet/packetengines/Kconfig +++ /dev/null @@ -1,44 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Packet Engines device configuration -# - -config NET_VENDOR_PACKET_ENGINES - bool "Packet Engines devices" - default y - depends on PCI - help - If you have a network (Ethernet) card belonging to this class, say Y. - - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about Packet Engines devices. If you say Y, you will - be asked for your specific card in the following questions. - -if NET_VENDOR_PACKET_ENGINES - -config HAMACHI - tristate "Packet Engines Hamachi GNIC-II support" - depends on PCI - select MII - help - If you have a Gigabit Ethernet card of this type, say Y here. - - To compile this driver as a module, choose M here. The module will be - called hamachi. - -config YELLOWFIN - tristate "Packet Engines Yellowfin Gigabit-NIC support" - depends on PCI - select CRC32 - help - Say Y here if you have a Packet Engines G-NIC PCI Gigabit Ethernet - adapter or the SYM53C885 Ethernet controller. The Gigabit adapter is - used by the Beowulf Linux cluster project. See - <http://cesdis.gsfc.nasa.gov/linux/drivers/yellowfin.html> for more - information about this driver in particular and Beowulf in general. - - To compile this driver as a module, choose M here: the module - will be called yellowfin. This is recommended. - -endif # NET_VENDOR_PACKET_ENGINES diff --git a/drivers/net/ethernet/packetengines/Makefile b/drivers/net/ethernet/packetengines/Makefile deleted file mode 100644 index cf054b796d11..000000000000 --- a/drivers/net/ethernet/packetengines/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the Packet Engines network device drivers. -# - -obj-$(CONFIG_HAMACHI) += hamachi.o -obj-$(CONFIG_YELLOWFIN) += yellowfin.o diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c deleted file mode 100644 index b0de7e9f12a5..000000000000 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ /dev/null @@ -1,1967 +0,0 @@ -/* hamachi.c: A Packet Engines GNIC-II Gigabit Ethernet driver for Linux. */ -/* - Written 1998-2000 by Donald Becker. - Updates 2000 by Keith Underwood. - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - This driver is for the Packet Engines GNIC-II PCI Gigabit Ethernet - adapter. - - Support and updates available at - http://www.scyld.com/network/hamachi.html - [link no longer provides useful info -jgarzik] - or - http://www.parl.clemson.edu/~keithu/hamachi.html - -*/ - -#define DRV_NAME "hamachi" -#define DRV_VERSION "2.1" -#define DRV_RELDATE "Sept 11, 2006" - - -/* A few user-configurable values. */ - -static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ -#define final_version -#define hamachi_debug debug -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 40; -static int mtu; -/* Default values selected by testing on a dual processor PIII-450 */ -/* These six interrupt control parameters may be set directly when loading the - * module, or through the rx_params and tx_params variables - */ -static int max_rx_latency = 0x11; -static int max_rx_gap = 0x05; -static int min_rx_pkt = 0x18; -static int max_tx_latency = 0x00; -static int max_tx_gap = 0x00; -static int min_tx_pkt = 0x30; - -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - -Setting to > 1518 causes all frames to be copied - -Setting to 0 disables copies -*/ -static int rx_copybreak; - -/* An override for the hardware detection of bus width. - Set to 1 to force 32 bit PCI bus detection. Set to 4 to force 64 bit. - Add 2 to disable parity detection. -*/ -static int force32; - - -/* Used to pass the media type, etc. - These exist for driver interoperability. - No media types are currently defined. - - The lower 4 bits are reserved for the media type. - - The next three bits may be set to one of the following: - 0x00000000 : Autodetect PCI bus - 0x00000010 : Force 32 bit PCI bus - 0x00000020 : Disable parity detection - 0x00000040 : Force 64 bit PCI bus - Default is autodetect - - The next bit can be used to force half-duplex. This is a bad - idea since no known implementations implement half-duplex, and, - in general, half-duplex for gigabit ethernet is a bad idea. - 0x00000080 : Force half-duplex - Default is full-duplex. - - In the original driver, the ninth bit could be used to force - full-duplex. Maintain that for compatibility - 0x00000200 : Force full-duplex -*/ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -/* The Hamachi chipset supports 3 parameters each for Rx and Tx - * interruput management. Parameters will be loaded as specified into - * the TxIntControl and RxIntControl registers. - * - * The registers are arranged as follows: - * 23 - 16 15 - 8 7 - 0 - * _________________________________ - * | min_pkt | max_gap | max_latency | - * --------------------------------- - * min_pkt : The minimum number of packets processed between - * interrupts. - * max_gap : The maximum inter-packet gap in units of 8.192 us - * max_latency : The absolute time between interrupts in units of 8.192 us - * - */ -static int rx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int tx_params[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Operational parameters that are set at compile time. */ - -/* Keep the ring sizes a power of two for compile efficiency. - The compiler will convert <unsigned>'%'<2^N> into a bit mask. - Making the Tx ring too large decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings, except for - excessive memory usage */ -/* Empirically it appears that the Tx ring needs to be a little bigger - for these Gbit adapters or you get into an overrun condition really - easily. Also, things appear to work a bit better in back-to-back - configurations if the Rx ring is 8 times the size of the Tx ring -*/ -#define TX_RING_SIZE 64 -#define RX_RING_SIZE 512 -#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct hamachi_desc) -#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct hamachi_desc) - -/* - * Enable netdev_ioctl. Added interrupt coalescing parameter adjustment. - * 2/19/99 Pete Wyckoff <wyckoff@ca.sandia.gov> - */ - -/* play with 64-bit addrlen; seems to be a teensy bit slower --pw */ -/* #define ADDRLEN 64 */ - -/* - * RX_CHECKSUM turns on card-generated receive checksum generation for - * TCP and UDP packets. Otherwise the upper layers do the calculation. - * 3/10/1999 Pete Wyckoff <wyckoff@ca.sandia.gov> - */ -#define RX_CHECKSUM - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (5*HZ) - -#include <linux/capability.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/time.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/interrupt.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/ethtool.h> -#include <linux/mii.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/ip.h> -#include <linux/delay.h> -#include <linux/bitops.h> - -#include <linux/uaccess.h> -#include <asm/processor.h> /* Processor type for cache alignment. */ -#include <asm/io.h> -#include <linux/unaligned.h> -#include <asm/cache.h> - -static const char version[] = -KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Written by Donald Becker\n" -" Some modifications by Eric kasten <kasten@nscl.msu.edu>\n" -" Further modifications by Keith Underwood <keithu@parl.clemson.edu>\n"; - - -/* IP_MF appears to be only defined in <netinet/ip.h>, however, - we need it for hardware checksumming support. FYI... some of - the definitions in <netinet/ip.h> conflict/duplicate those in - other linux headers causing many compiler warnings. -*/ -#ifndef IP_MF - #define IP_MF 0x2000 /* IP more frags from <netinet/ip.h> */ -#endif - -/* Define IP_OFFSET to be IPOPT_OFFSET */ -#ifndef IP_OFFSET - #ifdef IPOPT_OFFSET - #define IP_OFFSET IPOPT_OFFSET - #else - #define IP_OFFSET 2 - #endif -#endif - -#define RUN_AT(x) (jiffies + (x)) - -#ifndef ADDRLEN -#define ADDRLEN 32 -#endif - -/* Condensed bus+endian portability operations. */ -#if ADDRLEN == 64 -#define cpu_to_leXX(addr) cpu_to_le64(addr) -#define leXX_to_cpu(addr) le64_to_cpu(addr) -#else -#define cpu_to_leXX(addr) cpu_to_le32(addr) -#define leXX_to_cpu(addr) le32_to_cpu(addr) -#endif - - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the Packet Engines "Hamachi" -Gigabit Ethernet chip. The only PCA currently supported is the GNIC-II 64-bit -66Mhz PCI card. - -II. Board-specific settings - -No jumpers exist on the board. The chip supports software correction of -various motherboard wiring errors, however this driver does not support -that feature. - -III. Driver operation - -IIIa. Ring buffers - -The Hamachi uses a typical descriptor based bus-master architecture. -The descriptor list is similar to that used by the Digital Tulip. -This driver uses two statically allocated fixed-size descriptor lists -formed into rings by a branch from the final descriptor to the beginning of -the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. - -This driver uses a zero-copy receive and transmit scheme similar my other -network drivers. -The driver allocates full frame size skbuffs for the Rx ring buffers at -open() time and passes the skb->data field to the Hamachi as receive data -buffers. When an incoming frame is less than RX_COPYBREAK bytes long, -a fresh skbuff is allocated and the frame is copied to the new skbuff. -When the incoming frame is larger, the skbuff is passed directly up the -protocol stack and replaced by a newly allocated skbuff. - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. Gigabit cards are typically used on generously configured machines -and the underfilled buffers have negligible impact compared to the benefit of -a single allocation size, so the default value of zero results in never -copying packets. - -IIIb/c. Transmit/Receive Structure - -The Rx and Tx descriptor structure are straight-forward, with no historical -baggage that must be explained. Unlike the awkward DBDMA structure, there -are no unused fields or option bits that had only one allowable setting. - -Two details should be noted about the descriptors: The chip supports both 32 -bit and 64 bit address structures, and the length field is overwritten on -the receive descriptors. The descriptor length is set in the control word -for each channel. The development driver uses 32 bit addresses only, however -64 bit addresses may be enabled for 64 bit architectures e.g. the Alpha. - -IIId. Synchronization - -This driver is very similar to my other network drivers. -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'hmp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. After reaping the stats, it marks the Tx queue entry as -empty by incrementing the dirty_tx mark. Iff the 'hmp->tx_full' flag is set, it -clears both the tx_full and tbusy flags. - -IV. Notes - -Thanks to Kim Stearns of Packet Engines for providing a pair of GNIC-II boards. - -IVb. References - -Hamachi Engineering Design Specification, 5/15/97 -(Note: This version was marked "Confidential".) - -IVc. Errata - -None noted. - -V. Recent Changes - -01/15/1999 EPK Enlargement of the TX and RX ring sizes. This appears - to help avoid some stall conditions -- this needs further research. - -01/15/1999 EPK Creation of the hamachi_tx function. This function cleans - the Tx ring and is called from hamachi_start_xmit (this used to be - called from hamachi_interrupt but it tends to delay execution of the - interrupt handler and thus reduce bandwidth by reducing the latency - between hamachi_rx()'s). Notably, some modification has been made so - that the cleaning loop checks only to make sure that the DescOwn bit - isn't set in the status flag since the card is not required - to set the entire flag to zero after processing. - -01/15/1999 EPK In the hamachi_start_tx function, the Tx ring full flag is - checked before attempting to add a buffer to the ring. If the ring is full - an attempt is made to free any dirty buffers and thus find space for - the new buffer or the function returns non-zero which should case the - scheduler to reschedule the buffer later. - -01/15/1999 EPK Some adjustments were made to the chip initialization. - End-to-end flow control should now be fully active and the interrupt - algorithm vars have been changed. These could probably use further tuning. - -01/15/1999 EPK Added the max_{rx,tx}_latency options. These are used to - set the rx and tx latencies for the Hamachi interrupts. If you're having - problems with network stalls, try setting these to higher values. - Valid values are 0x00 through 0xff. - -01/15/1999 EPK In general, the overall bandwidth has increased and - latencies are better (sometimes by a factor of 2). Stalls are rare at - this point, however there still appears to be a bug somewhere between the - hardware and driver. TCP checksum errors under load also appear to be - eliminated at this point. - -01/18/1999 EPK Ensured that the DescEndRing bit was being set on both the - Rx and Tx rings. This appears to have been affecting whether a particular - peer-to-peer connection would hang under high load. I believe the Rx - rings was typically getting set correctly, but the Tx ring wasn't getting - the DescEndRing bit set during initialization. ??? Does this mean the - hamachi card is using the DescEndRing in processing even if a particular - slot isn't in use -- hypothetically, the card might be searching the - entire Tx ring for slots with the DescOwn bit set and then processing - them. If the DescEndRing bit isn't set, then it might just wander off - through memory until it hits a chunk of data with that bit set - and then looping back. - -02/09/1999 EPK Added Michel Mueller's TxDMA Interrupt and Tx-timeout - problem (TxCmd and RxCmd need only to be set when idle or stopped. - -02/09/1999 EPK Added code to check/reset dev->tbusy in hamachi_interrupt. - (Michel Mueller pointed out the ``permanently busy'' potential - problem here). - -02/22/1999 EPK Added Pete Wyckoff's ioctl to control the Tx/Rx latencies. - -02/23/1999 EPK Verified that the interrupt status field bits for Tx were - incorrectly defined and corrected (as per Michel Mueller). - -02/23/1999 EPK Corrected the Tx full check to check that at least 4 slots - were available before resetting the tbusy and tx_full flags - (as per Michel Mueller). - -03/11/1999 EPK Added Pete Wyckoff's hardware checksumming support. - -12/31/1999 KDU Cleaned up assorted things and added Don's code to force -32 bit. - -02/20/2000 KDU Some of the control was just plain odd. Cleaned up the -hamachi_start_xmit() and hamachi_interrupt() code. There is still some -re-structuring I would like to do. - -03/01/2000 KDU Experimenting with a WIDE range of interrupt mitigation -parameters on a dual P3-450 setup yielded the new default interrupt -mitigation parameters. Tx should interrupt VERY infrequently due to -Eric's scheme. Rx should be more often... - -03/13/2000 KDU Added a patch to make the Rx Checksum code interact -nicely with non-linux machines. - -03/13/2000 KDU Experimented with some of the configuration values: - - -It seems that enabling PCI performance commands for descriptors - (changing RxDMACtrl and TxDMACtrl lower nibble from 5 to D) has minimal - performance impact for any of my tests. (ttcp, netpipe, netperf) I will - leave them that way until I hear further feedback. - - -Increasing the PCI_LATENCY_TIMER to 130 - (2 + (burst size of 128 * (0 wait states + 1))) seems to slightly - degrade performance. Leaving default at 64 pending further information. - -03/14/2000 KDU Further tuning: - - -adjusted boguscnt in hamachi_rx() to depend on interrupt - mitigation parameters chosen. - - -Selected a set of interrupt parameters based on some extensive testing. - These may change with more testing. - -TO DO: - --Consider borrowing from the acenic driver code to check PCI_COMMAND for -PCI_COMMAND_INVALIDATE. Set maximum burst size to cache line size in -that case. - --fix the reset procedure. It doesn't quite work. -*/ - -/* A few values that may be tweaked. */ -/* Size of each temporary Rx buffer, calculated as: - * 1518 bytes (ethernet packet) + 2 bytes (to get 8 byte alignment for - * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum - */ -#define PKT_BUF_SZ 1536 - -/* For now, this is going to be set to the maximum size of an ethernet - * packet. Eventually, we may want to make it a variable that is - * related to the MTU - */ -#define MAX_FRAME_SIZE 1518 - -/* The rest of these values should never change. */ - -static void hamachi_timer(struct timer_list *t); - -enum capability_flags {CanHaveMII=1, }; -static const struct chip_info { - u16 vendor_id, device_id, device_id_mask, pad; - const char *name; - void (*media_timer)(struct timer_list *t); - int flags; -} chip_tbl[] = { - {0x1318, 0x0911, 0xffff, 0, "Hamachi GNIC-II", hamachi_timer, 0}, - {0,}, -}; - -/* Offsets to the Hamachi registers. Various sizes. */ -enum hamachi_offsets { - TxDMACtrl=0x00, TxCmd=0x04, TxStatus=0x06, TxPtr=0x08, TxCurPtr=0x10, - RxDMACtrl=0x20, RxCmd=0x24, RxStatus=0x26, RxPtr=0x28, RxCurPtr=0x30, - PCIClkMeas=0x060, MiscStatus=0x066, ChipRev=0x68, ChipReset=0x06B, - LEDCtrl=0x06C, VirtualJumpers=0x06D, GPIO=0x6E, - TxChecksum=0x074, RxChecksum=0x076, - TxIntrCtrl=0x078, RxIntrCtrl=0x07C, - InterruptEnable=0x080, InterruptClear=0x084, IntrStatus=0x088, - EventStatus=0x08C, - MACCnfg=0x0A0, FrameGap0=0x0A2, FrameGap1=0x0A4, - /* See enum MII_offsets below. */ - MACCnfg2=0x0B0, RxDepth=0x0B8, FlowCtrl=0x0BC, MaxFrameSize=0x0CE, - AddrMode=0x0D0, StationAddr=0x0D2, - /* Gigabit AutoNegotiation. */ - ANCtrl=0x0E0, ANStatus=0x0E2, ANXchngCtrl=0x0E4, ANAdvertise=0x0E8, - ANLinkPartnerAbility=0x0EA, - EECmdStatus=0x0F0, EEData=0x0F1, EEAddr=0x0F2, - FIFOcfg=0x0F8, -}; - -/* Offsets to the MII-mode registers. */ -enum MII_offsets { - MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC, - MII_Status=0xAE, -}; - -/* Bits in the interrupt status/mask registers. */ -enum intr_status_bits { - IntrRxDone=0x01, IntrRxPCIFault=0x02, IntrRxPCIErr=0x04, - IntrTxDone=0x100, IntrTxPCIFault=0x200, IntrTxPCIErr=0x400, - LinkChange=0x10000, NegotiationChange=0x20000, StatsMax=0x40000, }; - -/* The Hamachi Rx and Tx buffer descriptors. */ -struct hamachi_desc { - __le32 status_n_length; -#if ADDRLEN == 64 - u32 pad; - __le64 addr; -#else - __le32 addr; -#endif -}; - -/* Bits in hamachi_desc.status_n_length */ -enum desc_status_bits { - DescOwn=0x80000000, DescEndPacket=0x40000000, DescEndRing=0x20000000, - DescIntr=0x10000000, -}; - -#define PRIV_ALIGN 15 /* Required alignment mask */ -#define MII_CNT 4 -struct hamachi_private { - /* Descriptor rings first for alignment. Tx requires a second descriptor - for status. */ - struct hamachi_desc *rx_ring; - struct hamachi_desc *tx_ring; - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - dma_addr_t tx_ring_dma; - dma_addr_t rx_ring_dma; - struct timer_list timer; /* Media selection timer. */ - /* Frequently used and paired value: keep adjacent for cache effect. */ - spinlock_t lock; - int chip_id; - unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ - unsigned int cur_tx, dirty_tx; - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int duplex_lock:1; - unsigned int default_port:4; /* Last dev->if_port value. */ - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - struct mii_if_info mii_if; /* MII lib hooks/info */ - unsigned char phys[MII_CNT]; /* MII device addresses, only first one used. */ - u32 rx_int_var, tx_int_var; /* interrupt control variables */ - u32 option; /* Hold on to a copy of the options */ - struct pci_dev *pci_dev; - void __iomem *base; -}; - -MODULE_AUTHOR("Donald Becker <becker@scyld.com>, Eric Kasten <kasten@nscl.msu.edu>, Keith Underwood <keithu@parl.clemson.edu>"); -MODULE_DESCRIPTION("Packet Engines 'Hamachi' GNIC-II Gigabit Ethernet driver"); -MODULE_LICENSE("GPL"); - -module_param(max_interrupt_work, int, 0); -module_param(mtu, int, 0); -module_param(debug, int, 0); -module_param(min_rx_pkt, int, 0); -module_param(max_rx_gap, int, 0); -module_param(max_rx_latency, int, 0); -module_param(min_tx_pkt, int, 0); -module_param(max_tx_gap, int, 0); -module_param(max_tx_latency, int, 0); -module_param(rx_copybreak, int, 0); -module_param_array(rx_params, int, NULL, 0); -module_param_array(tx_params, int, NULL, 0); -module_param_array(options, int, NULL, 0); -module_param_array(full_duplex, int, NULL, 0); -module_param(force32, int, 0); -MODULE_PARM_DESC(max_interrupt_work, "GNIC-II maximum events handled per interrupt"); -MODULE_PARM_DESC(mtu, "GNIC-II MTU (all boards)"); -MODULE_PARM_DESC(debug, "GNIC-II debug level (0-7)"); -MODULE_PARM_DESC(min_rx_pkt, "GNIC-II minimum Rx packets processed between interrupts"); -MODULE_PARM_DESC(max_rx_gap, "GNIC-II maximum Rx inter-packet gap in 8.192 microsecond units"); -MODULE_PARM_DESC(max_rx_latency, "GNIC-II time between Rx interrupts in 8.192 microsecond units"); -MODULE_PARM_DESC(min_tx_pkt, "GNIC-II minimum Tx packets processed between interrupts"); -MODULE_PARM_DESC(max_tx_gap, "GNIC-II maximum Tx inter-packet gap in 8.192 microsecond units"); -MODULE_PARM_DESC(max_tx_latency, "GNIC-II time between Tx interrupts in 8.192 microsecond units"); -MODULE_PARM_DESC(rx_copybreak, "GNIC-II copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(rx_params, "GNIC-II min_rx_pkt+max_rx_gap+max_rx_latency"); -MODULE_PARM_DESC(tx_params, "GNIC-II min_tx_pkt+max_tx_gap+max_tx_latency"); -MODULE_PARM_DESC(options, "GNIC-II Bits 0-3: media type, bits 4-6: as force32, bit 7: half duplex, bit 9 full duplex"); -MODULE_PARM_DESC(full_duplex, "GNIC-II full duplex setting(s) (1)"); -MODULE_PARM_DESC(force32, "GNIC-II: Bit 0: 32 bit PCI, bit 1: disable parity, bit 2: 64 bit PCI (all boards)"); - -static int read_eeprom(void __iomem *ioaddr, int location); -static int mdio_read(struct net_device *dev, int phy_id, int location); -static void mdio_write(struct net_device *dev, int phy_id, int location, int value); -static int hamachi_open(struct net_device *dev); -static int hamachi_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int hamachi_siocdevprivate(struct net_device *dev, struct ifreq *rq, - void __user *data, int cmd); -static void hamachi_timer(struct timer_list *t); -static void hamachi_tx_timeout(struct net_device *dev, unsigned int txqueue); -static void hamachi_init_ring(struct net_device *dev); -static netdev_tx_t hamachi_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t hamachi_interrupt(int irq, void *dev_instance); -static int hamachi_rx(struct net_device *dev); -static inline int hamachi_tx(struct net_device *dev); -static void hamachi_error(struct net_device *dev, int intr_status); -static int hamachi_close(struct net_device *dev); -static struct net_device_stats *hamachi_get_stats(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; -static const struct ethtool_ops ethtool_ops_no_mii; - -static const struct net_device_ops hamachi_netdev_ops = { - .ndo_open = hamachi_open, - .ndo_stop = hamachi_close, - .ndo_start_xmit = hamachi_start_xmit, - .ndo_get_stats = hamachi_get_stats, - .ndo_set_rx_mode = set_rx_mode, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, - .ndo_tx_timeout = hamachi_tx_timeout, - .ndo_eth_ioctl = hamachi_ioctl, - .ndo_siocdevprivate = hamachi_siocdevprivate, -}; - - -static int hamachi_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct hamachi_private *hmp; - int option, i, rx_int_var, tx_int_var, boguscnt; - int chip_id = ent->driver_data; - int irq; - void __iomem *ioaddr; - unsigned long base; - static int card_idx; - struct net_device *dev; - void *ring_space; - dma_addr_t ring_dma; - int ret = -ENOMEM; - u8 addr[ETH_ALEN]; - -/* when built into the kernel, we only print version if device is found */ -#ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(version); -#endif - - if (pci_enable_device(pdev)) { - ret = -EIO; - goto err_out; - } - - base = pci_resource_start(pdev, 0); -#ifdef __alpha__ /* Really "64 bit addrs" */ - base |= (pci_resource_start(pdev, 1) << 32); -#endif - - pci_set_master(pdev); - - i = pci_request_regions(pdev, DRV_NAME); - if (i) - return i; - - irq = pdev->irq; - ioaddr = ioremap(base, 0x400); - if (!ioaddr) - goto err_out_release; - - dev = alloc_etherdev(sizeof(struct hamachi_private)); - if (!dev) - goto err_out_iounmap; - - SET_NETDEV_DEV(dev, &pdev->dev); - - for (i = 0; i < 6; i++) - addr[i] = read_eeprom(ioaddr, 4 + i); - eth_hw_addr_set(dev, addr); - -#if ! defined(final_version) - if (hamachi_debug > 4) - for (i = 0; i < 0x10; i++) - printk("%2.2x%s", - read_eeprom(ioaddr, i), i % 16 != 15 ? " " : "\n"); -#endif - - hmp = netdev_priv(dev); - spin_lock_init(&hmp->lock); - - hmp->mii_if.dev = dev; - hmp->mii_if.mdio_read = mdio_read; - hmp->mii_if.mdio_write = mdio_write; - hmp->mii_if.phy_id_mask = 0x1f; - hmp->mii_if.reg_num_mask = 0x1f; - - ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_cleardev; - hmp->tx_ring = ring_space; - hmp->tx_ring_dma = ring_dma; - - ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_unmap_tx; - hmp->rx_ring = ring_space; - hmp->rx_ring_dma = ring_dma; - - /* Check for options being passed in */ - option = card_idx < MAX_UNITS ? options[card_idx] : 0; - if (dev->mem_start) - option = dev->mem_start; - - /* If the bus size is misidentified, do the following. */ - force32 = force32 ? force32 : - ((option >= 0) ? ((option & 0x00000070) >> 4) : 0 ); - if (force32) - writeb(force32, ioaddr + VirtualJumpers); - - /* Hmmm, do we really need to reset the chip???. */ - writeb(0x01, ioaddr + ChipReset); - - /* After a reset, the clock speed measurement of the PCI bus will not - * be valid for a moment. Wait for a little while until it is. If - * it takes more than 10ms, forget it. - */ - udelay(10); - i = readb(ioaddr + PCIClkMeas); - for (boguscnt = 0; (!(i & 0x080)) && boguscnt < 1000; boguscnt++){ - udelay(10); - i = readb(ioaddr + PCIClkMeas); - } - - hmp->base = ioaddr; - pci_set_drvdata(pdev, dev); - - hmp->chip_id = chip_id; - hmp->pci_dev = pdev; - - /* The lower four bits are the media type. */ - if (option > 0) { - hmp->option = option; - if (option & 0x200) - hmp->mii_if.full_duplex = 1; - else if (option & 0x080) - hmp->mii_if.full_duplex = 0; - hmp->default_port = option & 15; - if (hmp->default_port) - hmp->mii_if.force_media = 1; - } - if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) - hmp->mii_if.full_duplex = 1; - - /* lock the duplex mode if someone specified a value */ - if (hmp->mii_if.full_duplex || (option & 0x080)) - hmp->duplex_lock = 1; - - /* Set interrupt tuning parameters */ - max_rx_latency = max_rx_latency & 0x00ff; - max_rx_gap = max_rx_gap & 0x00ff; - min_rx_pkt = min_rx_pkt & 0x00ff; - max_tx_latency = max_tx_latency & 0x00ff; - max_tx_gap = max_tx_gap & 0x00ff; - min_tx_pkt = min_tx_pkt & 0x00ff; - - rx_int_var = card_idx < MAX_UNITS ? rx_params[card_idx] : -1; - tx_int_var = card_idx < MAX_UNITS ? tx_params[card_idx] : -1; - hmp->rx_int_var = rx_int_var >= 0 ? rx_int_var : - (min_rx_pkt << 16 | max_rx_gap << 8 | max_rx_latency); - hmp->tx_int_var = tx_int_var >= 0 ? tx_int_var : - (min_tx_pkt << 16 | max_tx_gap << 8 | max_tx_latency); - - - /* The Hamachi-specific entries in the device structure. */ - dev->netdev_ops = &hamachi_netdev_ops; - dev->ethtool_ops = (chip_tbl[hmp->chip_id].flags & CanHaveMII) ? - ðtool_ops : ðtool_ops_no_mii; - dev->watchdog_timeo = TX_TIMEOUT; - if (mtu) - dev->mtu = mtu; - - i = register_netdev(dev); - if (i) { - ret = i; - goto err_out_unmap_rx; - } - - printk(KERN_INFO "%s: %s type %x at %p, %pM, IRQ %d.\n", - dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev), - ioaddr, dev->dev_addr, irq); - i = readb(ioaddr + PCIClkMeas); - printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers " - "%2.2x, LPA %4.4x.\n", - dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32, - i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers), - readw(ioaddr + ANLinkPartnerAbility)); - - if (chip_tbl[hmp->chip_id].flags & CanHaveMII) { - int phy, phy_idx = 0; - for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) { - int mii_status = mdio_read(dev, phy, MII_BMSR); - if (mii_status != 0xffff && - mii_status != 0x0000) { - hmp->phys[phy_idx++] = phy; - hmp->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE); - printk(KERN_INFO "%s: MII PHY found at address %d, status " - "0x%4.4x advertising %4.4x.\n", - dev->name, phy, mii_status, hmp->mii_if.advertising); - } - } - hmp->mii_cnt = phy_idx; - if (hmp->mii_cnt > 0) - hmp->mii_if.phy_id = hmp->phys[0]; - else - memset(&hmp->mii_if, 0, sizeof(hmp->mii_if)); - } - /* Configure gigabit autonegotiation. */ - writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */ - writew(0x08e0, ioaddr + ANAdvertise); /* Set our advertise word. */ - writew(0x1000, ioaddr + ANCtrl); /* Enable negotiation */ - - card_idx++; - return 0; - -err_out_unmap_rx: - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, hmp->rx_ring, - hmp->rx_ring_dma); -err_out_unmap_tx: - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, hmp->tx_ring, - hmp->tx_ring_dma); -err_out_cleardev: - free_netdev (dev); -err_out_iounmap: - iounmap(ioaddr); -err_out_release: - pci_release_regions(pdev); -err_out: - return ret; -} - -static int read_eeprom(void __iomem *ioaddr, int location) -{ - int bogus_cnt = 1000; - - /* We should check busy first - per docs -KDU */ - while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0); - writew(location, ioaddr + EEAddr); - writeb(0x02, ioaddr + EECmdStatus); - bogus_cnt = 1000; - while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0); - if (hamachi_debug > 5) - printk(" EEPROM status is %2.2x after %d ticks.\n", - (int)readb(ioaddr + EECmdStatus), 1000- bogus_cnt); - return readb(ioaddr + EEData); -} - -/* MII Managemen Data I/O accesses. - These routines assume the MDIO controller is idle, and do not exit until - the command is finished. */ - -static int mdio_read(struct net_device *dev, int phy_id, int location) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - int i; - - /* We should check busy first - per docs -KDU */ - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; - writew((phy_id<<8) + location, ioaddr + MII_Addr); - writew(0x0001, ioaddr + MII_Cmd); - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; - return readw(ioaddr + MII_Rd_Data); -} - -static void mdio_write(struct net_device *dev, int phy_id, int location, int value) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - int i; - - /* We should check busy first - per docs -KDU */ - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; - writew((phy_id<<8) + location, ioaddr + MII_Addr); - writew(value, ioaddr + MII_Wr_Data); - - /* Wait for the command to finish. */ - for (i = 10000; i >= 0; i--) - if ((readw(ioaddr + MII_Status) & 1) == 0) - break; -} - - -static int hamachi_open(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - int i; - u32 rx_int_var, tx_int_var; - u16 fifo_info; - - i = request_irq(hmp->pci_dev->irq, hamachi_interrupt, IRQF_SHARED, - dev->name, dev); - if (i) - return i; - - hamachi_init_ring(dev); - -#if ADDRLEN == 64 - /* writellll anyone ? */ - writel(hmp->rx_ring_dma, ioaddr + RxPtr); - writel(hmp->rx_ring_dma >> 32, ioaddr + RxPtr + 4); - writel(hmp->tx_ring_dma, ioaddr + TxPtr); - writel(hmp->tx_ring_dma >> 32, ioaddr + TxPtr + 4); -#else - writel(hmp->rx_ring_dma, ioaddr + RxPtr); - writel(hmp->tx_ring_dma, ioaddr + TxPtr); -#endif - - /* TODO: It would make sense to organize this as words since the card - * documentation does. -KDU - */ - for (i = 0; i < 6; i++) - writeb(dev->dev_addr[i], ioaddr + StationAddr + i); - - /* Initialize other registers: with so many this eventually this will - converted to an offset/value list. */ - - /* Configure the FIFO */ - fifo_info = (readw(ioaddr + GPIO) & 0x00C0) >> 6; - switch (fifo_info){ - case 0 : - /* No FIFO */ - writew(0x0000, ioaddr + FIFOcfg); - break; - case 1 : - /* Configure the FIFO for 512K external, 16K used for Tx. */ - writew(0x0028, ioaddr + FIFOcfg); - break; - case 2 : - /* Configure the FIFO for 1024 external, 32K used for Tx. */ - writew(0x004C, ioaddr + FIFOcfg); - break; - case 3 : - /* Configure the FIFO for 2048 external, 32K used for Tx. */ - writew(0x006C, ioaddr + FIFOcfg); - break; - default : - printk(KERN_WARNING "%s: Unsupported external memory config!\n", - dev->name); - /* Default to no FIFO */ - writew(0x0000, ioaddr + FIFOcfg); - break; - } - - if (dev->if_port == 0) - dev->if_port = hmp->default_port; - - - /* Setting the Rx mode will start the Rx process. */ - /* If someone didn't choose a duplex, default to full-duplex */ - if (hmp->duplex_lock != 1) - hmp->mii_if.full_duplex = 1; - - /* always 1, takes no more time to do it */ - writew(0x0001, ioaddr + RxChecksum); - writew(0x0000, ioaddr + TxChecksum); - writew(0x8000, ioaddr + MACCnfg); /* Soft reset the MAC */ - writew(0x215F, ioaddr + MACCnfg); - writew(0x000C, ioaddr + FrameGap0); - /* WHAT?!?!? Why isn't this documented somewhere? -KDU */ - writew(0x1018, ioaddr + FrameGap1); - /* Why do we enable receives/transmits here? -KDU */ - writew(0x0780, ioaddr + MACCnfg2); /* Upper 16 bits control LEDs. */ - /* Enable automatic generation of flow control frames, period 0xffff. */ - writel(0x0030FFFF, ioaddr + FlowCtrl); - writew(MAX_FRAME_SIZE, ioaddr + MaxFrameSize); /* dev->mtu+14 ??? */ - - /* Enable legacy links. */ - writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */ - /* Initial Link LED to blinking red. */ - writeb(0x03, ioaddr + LEDCtrl); - - /* Configure interrupt mitigation. This has a great effect on - performance, so systems tuning should start here!. */ - - rx_int_var = hmp->rx_int_var; - tx_int_var = hmp->tx_int_var; - - if (hamachi_debug > 1) { - printk("max_tx_latency: %d, max_tx_gap: %d, min_tx_pkt: %d\n", - tx_int_var & 0x00ff, (tx_int_var & 0x00ff00) >> 8, - (tx_int_var & 0x00ff0000) >> 16); - printk("max_rx_latency: %d, max_rx_gap: %d, min_rx_pkt: %d\n", - rx_int_var & 0x00ff, (rx_int_var & 0x00ff00) >> 8, - (rx_int_var & 0x00ff0000) >> 16); - printk("rx_int_var: %x, tx_int_var: %x\n", rx_int_var, tx_int_var); - } - - writel(tx_int_var, ioaddr + TxIntrCtrl); - writel(rx_int_var, ioaddr + RxIntrCtrl); - - set_rx_mode(dev); - - netif_start_queue(dev); - - /* Enable interrupts by setting the interrupt mask. */ - writel(0x80878787, ioaddr + InterruptEnable); - writew(0x0000, ioaddr + EventStatus); /* Clear non-interrupting events */ - - /* Configure and start the DMA channels. */ - /* Burst sizes are in the low three bits: size = 4<<(val&7) */ -#if ADDRLEN == 64 - writew(0x005D, ioaddr + RxDMACtrl); /* 128 dword bursts */ - writew(0x005D, ioaddr + TxDMACtrl); -#else - writew(0x001D, ioaddr + RxDMACtrl); - writew(0x001D, ioaddr + TxDMACtrl); -#endif - writew(0x0001, ioaddr + RxCmd); - - if (hamachi_debug > 2) { - printk(KERN_DEBUG "%s: Done hamachi_open(), status: Rx %x Tx %x.\n", - dev->name, readw(ioaddr + RxStatus), readw(ioaddr + TxStatus)); - } - /* Set the timer to check for link beat. */ - timer_setup(&hmp->timer, hamachi_timer, 0); - hmp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ - add_timer(&hmp->timer); - - return 0; -} - -static inline int hamachi_tx(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - - /* Update the dirty pointer until we find an entry that is - still owned by the card */ - for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++) { - int entry = hmp->dirty_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) - break; - /* Free the original skb. */ - skb = hmp->tx_skbuff[entry]; - if (skb) { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[entry].addr), - skb->len, DMA_TO_DEVICE); - dev_kfree_skb(skb); - hmp->tx_skbuff[entry] = NULL; - } - hmp->tx_ring[entry].status_n_length = 0; - if (entry >= TX_RING_SIZE-1) - hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= - cpu_to_le32(DescEndRing); - dev->stats.tx_packets++; - } - - return 0; -} - -static void hamachi_timer(struct timer_list *t) -{ - struct hamachi_private *hmp = timer_container_of(hmp, t, timer); - struct net_device *dev = hmp->mii_if.dev; - void __iomem *ioaddr = hmp->base; - int next_tick = 10*HZ; - - if (hamachi_debug > 2) { - printk(KERN_INFO "%s: Hamachi Autonegotiation status %4.4x, LPA " - "%4.4x.\n", dev->name, readw(ioaddr + ANStatus), - readw(ioaddr + ANLinkPartnerAbility)); - printk(KERN_INFO "%s: Autonegotiation regs %4.4x %4.4x %4.4x " - "%4.4x %4.4x %4.4x.\n", dev->name, - readw(ioaddr + 0x0e0), - readw(ioaddr + 0x0e2), - readw(ioaddr + 0x0e4), - readw(ioaddr + 0x0e6), - readw(ioaddr + 0x0e8), - readw(ioaddr + 0x0eA)); - } - /* We could do something here... nah. */ - hmp->timer.expires = RUN_AT(next_tick); - add_timer(&hmp->timer); -} - -static void hamachi_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - int i; - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - printk(KERN_WARNING "%s: Hamachi transmit timed out, status %8.8x," - " resetting...\n", dev->name, (int)readw(ioaddr + TxStatus)); - - { - printk(KERN_DEBUG " Rx ring %p: ", hmp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(KERN_CONT " %8.8x", - le32_to_cpu(hmp->rx_ring[i].status_n_length)); - printk(KERN_CONT "\n"); - printk(KERN_DEBUG" Tx ring %p: ", hmp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(KERN_CONT " %4.4x", - le32_to_cpu(hmp->tx_ring[i].status_n_length)); - printk(KERN_CONT "\n"); - } - - /* Reinit the hardware and make sure the Rx and Tx processes - are up and running. - */ - dev->if_port = 0; - /* The right way to do Reset. -KDU - * -Clear OWN bit in all Rx/Tx descriptors - * -Wait 50 uS for channels to go idle - * -Turn off MAC receiver - * -Issue Reset - */ - - for (i = 0; i < RX_RING_SIZE; i++) - hmp->rx_ring[i].status_n_length &= cpu_to_le32(~DescOwn); - - /* Presume that all packets in the Tx queue are gone if we have to - * re-init the hardware. - */ - for (i = 0; i < TX_RING_SIZE; i++){ - struct sk_buff *skb; - - if (i >= TX_RING_SIZE - 1) - hmp->tx_ring[i].status_n_length = - cpu_to_le32(DescEndRing) | - (hmp->tx_ring[i].status_n_length & - cpu_to_le32(0x0000ffff)); - else - hmp->tx_ring[i].status_n_length &= cpu_to_le32(0x0000ffff); - skb = hmp->tx_skbuff[i]; - if (skb){ - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[i].addr), - skb->len, DMA_TO_DEVICE); - dev_kfree_skb(skb); - hmp->tx_skbuff[i] = NULL; - } - } - - udelay(60); /* Sleep 60 us just for safety sake */ - writew(0x0002, ioaddr + RxCmd); /* STOP Rx */ - - writeb(0x01, ioaddr + ChipReset); /* Reinit the hardware */ - - hmp->tx_full = 0; - hmp->cur_rx = hmp->cur_tx = 0; - hmp->dirty_rx = hmp->dirty_tx = 0; - /* Rx packets are also presumed lost; however, we need to make sure a - * ring of buffers is in tact. -KDU - */ - for (i = 0; i < RX_RING_SIZE; i++){ - struct sk_buff *skb = hmp->rx_skbuff[i]; - - if (skb){ - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[i].addr), - hmp->rx_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb(skb); - hmp->rx_skbuff[i] = NULL; - } - } - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb; - - skb = netdev_alloc_skb_ip_align(dev, hmp->rx_buf_sz); - hmp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - - hmp->rx_ring[i].addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - hmp->rx_buf_sz, - DMA_FROM_DEVICE)); - hmp->rx_ring[i].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescIntr | (hmp->rx_buf_sz - 2)); - } - hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - /* Mark the last entry as wrapping the ring. */ - hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); - - /* Trigger an immediate transmit demand. */ - netif_trans_update(dev); /* prevent tx timeout */ - dev->stats.tx_errors++; - - /* Restart the chip's Tx/Rx processes . */ - writew(0x0002, ioaddr + TxCmd); /* STOP Tx */ - writew(0x0001, ioaddr + TxCmd); /* START Tx */ - writew(0x0001, ioaddr + RxCmd); /* START Rx */ - - netif_wake_queue(dev); -} - - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static void hamachi_init_ring(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - int i; - - hmp->tx_full = 0; - hmp->cur_rx = hmp->cur_tx = 0; - hmp->dirty_rx = hmp->dirty_tx = 0; - - /* +26 gets the maximum ethernet encapsulation, +7 & ~7 because the - * card needs room to do 8 byte alignment, +2 so we can reserve - * the first 2 bytes, and +16 gets room for the status word from the - * card. -KDU - */ - hmp->rx_buf_sz = (dev->mtu <= 1492 ? PKT_BUF_SZ : - (((dev->mtu+26+7) & ~7) + 16)); - - /* Initialize all Rx descriptors. */ - for (i = 0; i < RX_RING_SIZE; i++) { - hmp->rx_ring[i].status_n_length = 0; - hmp->rx_skbuff[i] = NULL; - } - /* Fill in the Rx buffers. Handle allocation failure gracefully. */ - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = netdev_alloc_skb(dev, hmp->rx_buf_sz + 2); - hmp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb_reserve(skb, 2); /* 16 byte align the IP header. */ - hmp->rx_ring[i].addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - hmp->rx_buf_sz, - DMA_FROM_DEVICE)); - /* -2 because it doesn't REALLY have that first 2 bytes -KDU */ - hmp->rx_ring[i].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescIntr | (hmp->rx_buf_sz -2)); - } - hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); - - for (i = 0; i < TX_RING_SIZE; i++) { - hmp->tx_skbuff[i] = NULL; - hmp->tx_ring[i].status_n_length = 0; - } - /* Mark the last entry of the ring */ - hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing); -} - - -static netdev_tx_t hamachi_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - unsigned entry; - u16 status; - - /* Ok, now make sure that the queue has space before trying to - add another skbuff. if we return non-zero the scheduler - should interpret this as a queue full and requeue the buffer - for later. - */ - if (hmp->tx_full) { - /* We should NEVER reach this point -KDU */ - printk(KERN_WARNING "%s: Hamachi transmit queue full at slot %d.\n",dev->name, hmp->cur_tx); - - /* Wake the potentially-idle transmit channel. */ - /* If we don't need to read status, DON'T -KDU */ - status=readw(hmp->base + TxStatus); - if( !(status & 0x0001) || (status & 0x0002)) - writew(0x0001, hmp->base + TxCmd); - return NETDEV_TX_BUSY; - } - - /* Caution: the write order is important here, set the field - with the "ownership" bits last. */ - - /* Calculate the next Tx descriptor entry. */ - entry = hmp->cur_tx % TX_RING_SIZE; - - hmp->tx_skbuff[entry] = skb; - - hmp->tx_ring[entry].addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - skb->len, - DMA_TO_DEVICE)); - - /* Hmmmm, could probably put a DescIntr on these, but the way - the driver is currently coded makes Tx interrupts unnecessary - since the clearing of the Tx ring is handled by the start_xmit - routine. This organization helps mitigate the interrupts a - bit and probably renders the max_tx_latency param useless. - - Update: Putting a DescIntr bit on all of the descriptors and - mitigating interrupt frequency with the tx_min_pkt parameter. -KDU - */ - if (entry >= TX_RING_SIZE-1) /* Wrap ring */ - hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescEndRing | DescIntr | skb->len); - else - hmp->tx_ring[entry].status_n_length = cpu_to_le32(DescOwn | - DescEndPacket | DescIntr | skb->len); - hmp->cur_tx++; - - /* Non-x86 Todo: explicitly flush cache lines here. */ - - /* Wake the potentially-idle transmit channel. */ - /* If we don't need to read status, DON'T -KDU */ - status=readw(hmp->base + TxStatus); - if( !(status & 0x0001) || (status & 0x0002)) - writew(0x0001, hmp->base + TxCmd); - - /* Immediately before returning, let's clear as many entries as we can. */ - hamachi_tx(dev); - - /* We should kick the bottom half here, since we are not accepting - * interrupts with every packet. i.e. realize that Gigabit ethernet - * can transmit faster than ordinary machines can load packets; - * hence, any packet that got put off because we were in the transmit - * routine should IMMEDIATELY get a chance to be re-queued. -KDU - */ - if ((hmp->cur_tx - hmp->dirty_tx) < (TX_RING_SIZE - 4)) - netif_wake_queue(dev); /* Typical path */ - else { - hmp->tx_full = 1; - netif_stop_queue(dev); - } - - if (hamachi_debug > 4) { - printk(KERN_DEBUG "%s: Hamachi transmit frame #%d queued in slot %d.\n", - dev->name, hmp->cur_tx, entry); - } - return NETDEV_TX_OK; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static irqreturn_t hamachi_interrupt(int irq, void *dev_instance) -{ - struct net_device *dev = dev_instance; - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - long boguscnt = max_interrupt_work; - int handled = 0; - -#ifndef final_version /* Can never occur. */ - if (dev == NULL) { - printk (KERN_ERR "hamachi_interrupt(): irq %d for unknown device.\n", irq); - return IRQ_NONE; - } -#endif - - spin_lock(&hmp->lock); - - do { - u32 intr_status = readl(ioaddr + InterruptClear); - - if (hamachi_debug > 4) - printk(KERN_DEBUG "%s: Hamachi interrupt, status %4.4x.\n", - dev->name, intr_status); - - if (intr_status == 0) - break; - - handled = 1; - - if (intr_status & IntrRxDone) - hamachi_rx(dev); - - if (intr_status & IntrTxDone){ - /* This code should RARELY need to execute. After all, this is - * a gigabit link, it should consume packets as fast as we put - * them in AND we clear the Tx ring in hamachi_start_xmit(). - */ - if (hmp->tx_full){ - for (; hmp->cur_tx - hmp->dirty_tx > 0; hmp->dirty_tx++){ - int entry = hmp->dirty_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (hmp->tx_ring[entry].status_n_length & cpu_to_le32(DescOwn)) - break; - skb = hmp->tx_skbuff[entry]; - /* Free the original skb. */ - if (skb){ - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[entry].addr), - skb->len, - DMA_TO_DEVICE); - dev_consume_skb_irq(skb); - hmp->tx_skbuff[entry] = NULL; - } - hmp->tx_ring[entry].status_n_length = 0; - if (entry >= TX_RING_SIZE-1) - hmp->tx_ring[TX_RING_SIZE-1].status_n_length |= - cpu_to_le32(DescEndRing); - dev->stats.tx_packets++; - } - if (hmp->cur_tx - hmp->dirty_tx < TX_RING_SIZE - 4){ - /* The ring is no longer full */ - hmp->tx_full = 0; - netif_wake_queue(dev); - } - } else { - netif_wake_queue(dev); - } - } - - - /* Abnormal error summary/uncommon events handlers. */ - if (intr_status & - (IntrTxPCIFault | IntrTxPCIErr | IntrRxPCIFault | IntrRxPCIErr | - LinkChange | NegotiationChange | StatsMax)) - hamachi_error(dev, intr_status); - - if (--boguscnt < 0) { - printk(KERN_WARNING "%s: Too much work at interrupt, status=0x%4.4x.\n", - dev->name, intr_status); - break; - } - } while (1); - - if (hamachi_debug > 3) - printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", - dev->name, readl(ioaddr + IntrStatus)); - -#ifndef final_version - /* Code that should never be run! Perhaps remove after testing.. */ - { - static int stopit = 10; - if (dev->start == 0 && --stopit < 0) { - printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n", - dev->name); - free_irq(irq, dev); - } - } -#endif - - spin_unlock(&hmp->lock); - return IRQ_RETVAL(handled); -} - -/* This routine is logically part of the interrupt handler, but separated - for clarity and better register allocation. */ -static int hamachi_rx(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - int entry = hmp->cur_rx % RX_RING_SIZE; - int boguscnt = (hmp->dirty_rx + RX_RING_SIZE) - hmp->cur_rx; - - if (hamachi_debug > 4) { - printk(KERN_DEBUG " In hamachi_rx(), entry %d status %4.4x.\n", - entry, hmp->rx_ring[entry].status_n_length); - } - - /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (1) { - struct hamachi_desc *desc = &(hmp->rx_ring[entry]); - u32 desc_status = le32_to_cpu(desc->status_n_length); - u16 data_size = desc_status; /* Implicit truncate */ - u8 *buf_addr; - s32 frame_status; - - if (desc_status & DescOwn) - break; - dma_sync_single_for_cpu(&hmp->pci_dev->dev, - leXX_to_cpu(desc->addr), - hmp->rx_buf_sz, DMA_FROM_DEVICE); - buf_addr = (u8 *) hmp->rx_skbuff[entry]->data; - frame_status = get_unaligned_le32(&(buf_addr[data_size - 12])); - if (hamachi_debug > 4) - printk(KERN_DEBUG " hamachi_rx() status was %8.8x.\n", - frame_status); - if (--boguscnt < 0) - break; - if ( ! (desc_status & DescEndPacket)) { - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, entry %#x length %d status %4.4x!\n", - dev->name, hmp->cur_rx, data_size, desc_status); - printk(KERN_WARNING "%s: Oversized Ethernet frame %p vs %p.\n", - dev->name, desc, &hmp->rx_ring[hmp->cur_rx % RX_RING_SIZE]); - printk(KERN_WARNING "%s: Oversized Ethernet frame -- next status %x/%x last status %x.\n", - dev->name, - le32_to_cpu(hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length) & 0xffff0000, - le32_to_cpu(hmp->rx_ring[(hmp->cur_rx+1) % RX_RING_SIZE].status_n_length) & 0x0000ffff, - le32_to_cpu(hmp->rx_ring[(hmp->cur_rx-1) % RX_RING_SIZE].status_n_length)); - dev->stats.rx_length_errors++; - } /* else Omit for prototype errata??? */ - if (frame_status & 0x00380000) { - /* There was an error. */ - if (hamachi_debug > 2) - printk(KERN_DEBUG " hamachi_rx() Rx error was %8.8x.\n", - frame_status); - dev->stats.rx_errors++; - if (frame_status & 0x00600000) - dev->stats.rx_length_errors++; - if (frame_status & 0x00080000) - dev->stats.rx_frame_errors++; - if (frame_status & 0x00100000) - dev->stats.rx_crc_errors++; - if (frame_status < 0) - dev->stats.rx_dropped++; - } else { - struct sk_buff *skb; - /* Omit CRC */ - u16 pkt_len = (frame_status & 0x07ff) - 4; -#ifdef RX_CHECKSUM - u32 pfck = *(u32 *) &buf_addr[data_size - 8]; -#endif - - -#ifndef final_version - if (hamachi_debug > 4) - printk(KERN_DEBUG " hamachi_rx() normal Rx pkt length %d" - " of %d, bogus_cnt %d.\n", - pkt_len, data_size, boguscnt); - if (hamachi_debug > 5) - printk(KERN_DEBUG"%s: rx status %8.8x %8.8x %8.8x %8.8x %8.8x.\n", - dev->name, - *(s32*)&(buf_addr[data_size - 20]), - *(s32*)&(buf_addr[data_size - 16]), - *(s32*)&(buf_addr[data_size - 12]), - *(s32*)&(buf_addr[data_size - 8]), - *(s32*)&(buf_addr[data_size - 4])); -#endif - /* Check if the packet is long enough to accept without copying - to a minimally-sized skbuff. */ - if (pkt_len < rx_copybreak && - (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { -#ifdef RX_CHECKSUM - printk(KERN_ERR "%s: rx_copybreak non-zero " - "not good with RX_CHECKSUM\n", dev->name); -#endif - skb_reserve(skb, 2); /* 16 byte align the IP header */ - dma_sync_single_for_cpu(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[entry].addr), - hmp->rx_buf_sz, - DMA_FROM_DEVICE); - /* Call copy + cksum if available. */ -#if 1 || USE_IP_COPYSUM - skb_copy_to_linear_data(skb, - hmp->rx_skbuff[entry]->data, pkt_len); - skb_put(skb, pkt_len); -#else - skb_put_data(skb, hmp->rx_ring_dma - + entry*sizeof(*desc), pkt_len); -#endif - dma_sync_single_for_device(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[entry].addr), - hmp->rx_buf_sz, - DMA_FROM_DEVICE); - } else { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[entry].addr), - hmp->rx_buf_sz, - DMA_FROM_DEVICE); - skb_put(skb = hmp->rx_skbuff[entry], pkt_len); - hmp->rx_skbuff[entry] = NULL; - } - skb->protocol = eth_type_trans(skb, dev); - - -#ifdef RX_CHECKSUM - /* TCP or UDP on ipv4, DIX encoding */ - if (pfck>>24 == 0x91 || pfck>>24 == 0x51) { - struct iphdr *ih = (struct iphdr *) skb->data; - /* Check that IP packet is at least 46 bytes, otherwise, - * there may be pad bytes included in the hardware checksum. - * This wouldn't happen if everyone padded with 0. - */ - if (ntohs(ih->tot_len) >= 46){ - /* don't worry about frags */ - if (!(ih->frag_off & cpu_to_be16(IP_MF|IP_OFFSET))) { - u32 inv = *(u32 *) &buf_addr[data_size - 16]; - u32 *p = (u32 *) &buf_addr[data_size - 20]; - register u32 crc, p_r, p_r1; - - if (inv & 4) { - inv &= ~4; - --p; - } - p_r = *p; - p_r1 = *(p-1); - switch (inv) { - case 0: - crc = (p_r & 0xffff) + (p_r >> 16); - break; - case 1: - crc = (p_r >> 16) + (p_r & 0xffff) - + (p_r1 >> 16 & 0xff00); - break; - case 2: - crc = p_r + (p_r1 >> 16); - break; - case 3: - crc = p_r + (p_r1 & 0xff00) + (p_r1 >> 16); - break; - default: /*NOTREACHED*/ crc = 0; - } - if (crc & 0xffff0000) { - crc &= 0xffff; - ++crc; - } - /* tcp/udp will add in pseudo */ - skb->csum = ntohs(pfck & 0xffff); - if (skb->csum > crc) - skb->csum -= crc; - else - skb->csum += (~crc & 0xffff); - /* - * could do the pseudo myself and return - * CHECKSUM_UNNECESSARY - */ - skb->ip_summed = CHECKSUM_COMPLETE; - } - } - } -#endif /* RX_CHECKSUM */ - - netif_rx(skb); - dev->stats.rx_packets++; - } - entry = (++hmp->cur_rx) % RX_RING_SIZE; - } - - /* Refill the Rx ring buffers. */ - for (; hmp->cur_rx - hmp->dirty_rx > 0; hmp->dirty_rx++) { - struct hamachi_desc *desc; - - entry = hmp->dirty_rx % RX_RING_SIZE; - desc = &(hmp->rx_ring[entry]); - if (hmp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb = netdev_alloc_skb(dev, hmp->rx_buf_sz + 2); - - hmp->rx_skbuff[entry] = skb; - if (skb == NULL) - break; /* Better luck next round. */ - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - desc->addr = cpu_to_leXX(dma_map_single(&hmp->pci_dev->dev, - skb->data, - hmp->rx_buf_sz, - DMA_FROM_DEVICE)); - } - desc->status_n_length = cpu_to_le32(hmp->rx_buf_sz); - if (entry >= RX_RING_SIZE-1) - desc->status_n_length |= cpu_to_le32(DescOwn | - DescEndPacket | DescEndRing | DescIntr); - else - desc->status_n_length |= cpu_to_le32(DescOwn | - DescEndPacket | DescIntr); - } - - /* Restart Rx engine if stopped. */ - /* If we don't need to check status, don't. -KDU */ - if (readw(hmp->base + RxStatus) & 0x0002) - writew(0x0001, hmp->base + RxCmd); - - return 0; -} - -/* This is more properly named "uncommon interrupt events", as it covers more - than just errors. */ -static void hamachi_error(struct net_device *dev, int intr_status) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - if (intr_status & (LinkChange|NegotiationChange)) { - if (hamachi_debug > 1) - printk(KERN_INFO "%s: Link changed: AutoNegotiation Ctrl" - " %4.4x, Status %4.4x %4.4x Intr status %4.4x.\n", - dev->name, readw(ioaddr + 0x0E0), readw(ioaddr + 0x0E2), - readw(ioaddr + ANLinkPartnerAbility), - readl(ioaddr + IntrStatus)); - if (readw(ioaddr + ANStatus) & 0x20) - writeb(0x01, ioaddr + LEDCtrl); - else - writeb(0x03, ioaddr + LEDCtrl); - } - if (intr_status & StatsMax) { - hamachi_get_stats(dev); - /* Read the overflow bits to clear. */ - readl(ioaddr + 0x370); - readl(ioaddr + 0x3F0); - } - if ((intr_status & ~(LinkChange|StatsMax|NegotiationChange|IntrRxDone|IntrTxDone)) && - hamachi_debug) - printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", - dev->name, intr_status); - /* Hmmmmm, it's not clear how to recover from PCI faults. */ - if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) - dev->stats.tx_fifo_errors++; - if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) - dev->stats.rx_fifo_errors++; -} - -static int hamachi_close(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - struct sk_buff *skb; - int i; - - netif_stop_queue(dev); - - if (hamachi_debug > 1) { - printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %4.4x Rx %4.4x Int %2.2x.\n", - dev->name, readw(ioaddr + TxStatus), - readw(ioaddr + RxStatus), readl(ioaddr + IntrStatus)); - printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", - dev->name, hmp->cur_tx, hmp->dirty_tx, hmp->cur_rx, hmp->dirty_rx); - } - - /* Disable interrupts by clearing the interrupt mask. */ - writel(0x0000, ioaddr + InterruptEnable); - - /* Stop the chip's Tx and Rx processes. */ - writel(2, ioaddr + RxCmd); - writew(2, ioaddr + TxCmd); - -#ifdef __i386__ - if (hamachi_debug > 2) { - printk(KERN_DEBUG " Tx ring at %8.8x:\n", - (int)hmp->tx_ring_dma); - for (i = 0; i < TX_RING_SIZE; i++) - printk(KERN_DEBUG " %c #%d desc. %8.8x %8.8x.\n", - readl(ioaddr + TxCurPtr) == (long)&hmp->tx_ring[i] ? '>' : ' ', - i, hmp->tx_ring[i].status_n_length, hmp->tx_ring[i].addr); - printk(KERN_DEBUG " Rx ring %8.8x:\n", - (int)hmp->rx_ring_dma); - for (i = 0; i < RX_RING_SIZE; i++) { - printk(KERN_DEBUG " %c #%d desc. %4.4x %8.8x\n", - readl(ioaddr + RxCurPtr) == (long)&hmp->rx_ring[i] ? '>' : ' ', - i, hmp->rx_ring[i].status_n_length, hmp->rx_ring[i].addr); - if (hamachi_debug > 6) { - if (*(u8*)hmp->rx_skbuff[i]->data != 0x69) { - u16 *addr = (u16 *) - hmp->rx_skbuff[i]->data; - int j; - printk(KERN_DEBUG "Addr: "); - for (j = 0; j < 0x50; j++) - printk(" %4.4x", addr[j]); - printk("\n"); - } - } - } - } -#endif /* __i386__ debugging only */ - - free_irq(hmp->pci_dev->irq, dev); - - timer_delete_sync(&hmp->timer); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - skb = hmp->rx_skbuff[i]; - hmp->rx_ring[i].status_n_length = 0; - if (skb) { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->rx_ring[i].addr), - hmp->rx_buf_sz, DMA_FROM_DEVICE); - dev_kfree_skb(skb); - hmp->rx_skbuff[i] = NULL; - } - hmp->rx_ring[i].addr = cpu_to_leXX(0xBADF00D0); /* An invalid address. */ - } - for (i = 0; i < TX_RING_SIZE; i++) { - skb = hmp->tx_skbuff[i]; - if (skb) { - dma_unmap_single(&hmp->pci_dev->dev, - leXX_to_cpu(hmp->tx_ring[i].addr), - skb->len, DMA_TO_DEVICE); - dev_kfree_skb(skb); - hmp->tx_skbuff[i] = NULL; - } - } - - writeb(0x00, ioaddr + LEDCtrl); - - return 0; -} - -static struct net_device_stats *hamachi_get_stats(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - /* We should lock this segment of code for SMP eventually, although - the vulnerability window is very small and statistics are - non-critical. */ - /* Ok, what goes here? This appears to be stuck at 21 packets - according to ifconfig. It does get incremented in hamachi_tx(), - so I think I'll comment it out here and see if better things - happen. - */ - /* dev->stats.tx_packets = readl(ioaddr + 0x000); */ - - /* Total Uni+Brd+Multi */ - dev->stats.rx_bytes = readl(ioaddr + 0x330); - /* Total Uni+Brd+Multi */ - dev->stats.tx_bytes = readl(ioaddr + 0x3B0); - /* Multicast Rx */ - dev->stats.multicast = readl(ioaddr + 0x320); - - /* Over+Undersized */ - dev->stats.rx_length_errors = readl(ioaddr + 0x368); - /* Jabber */ - dev->stats.rx_over_errors = readl(ioaddr + 0x35C); - /* Jabber */ - dev->stats.rx_crc_errors = readl(ioaddr + 0x360); - /* Symbol Errs */ - dev->stats.rx_frame_errors = readl(ioaddr + 0x364); - /* Dropped */ - dev->stats.rx_missed_errors = readl(ioaddr + 0x36C); - - return &dev->stats; -} - -static void set_rx_mode(struct net_device *dev) -{ - struct hamachi_private *hmp = netdev_priv(dev); - void __iomem *ioaddr = hmp->base; - - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - writew(0x000F, ioaddr + AddrMode); - } else if ((netdev_mc_count(dev) > 63) || (dev->flags & IFF_ALLMULTI)) { - /* Too many to match, or accept all multicasts. */ - writew(0x000B, ioaddr + AddrMode); - } else if (!netdev_mc_empty(dev)) { /* Must use the CAM filter. */ - struct netdev_hw_addr *ha; - int i = 0; - - netdev_for_each_mc_addr(ha, dev) { - writel(*(u32 *)(ha->addr), ioaddr + 0x100 + i*8); - writel(0x20000 | (*(u16 *)&ha->addr[4]), - ioaddr + 0x104 + i*8); - i++; - } - /* Clear remaining entries. */ - for (; i < 64; i++) - writel(0, ioaddr + 0x104 + i*8); - writew(0x0003, ioaddr + AddrMode); - } else { /* Normal, unicast/broadcast-only mode. */ - writew(0x0001, ioaddr + AddrMode); - } -} - -static int check_if_running(struct net_device *dev) -{ - if (!netif_running(dev)) - return -EINVAL; - return 0; -} - -static void hamachi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct hamachi_private *np = netdev_priv(dev); - - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); - strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); -} - -static int hamachi_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - spin_lock_irq(&np->lock); - mii_ethtool_get_link_ksettings(&np->mii_if, cmd); - spin_unlock_irq(&np->lock); - return 0; -} - -static int hamachi_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - int res; - spin_lock_irq(&np->lock); - res = mii_ethtool_set_link_ksettings(&np->mii_if, cmd); - spin_unlock_irq(&np->lock); - return res; -} - -static int hamachi_nway_reset(struct net_device *dev) -{ - struct hamachi_private *np = netdev_priv(dev); - return mii_nway_restart(&np->mii_if); -} - -static u32 hamachi_get_link(struct net_device *dev) -{ - struct hamachi_private *np = netdev_priv(dev); - return mii_link_ok(&np->mii_if); -} - -static const struct ethtool_ops ethtool_ops = { - .begin = check_if_running, - .get_drvinfo = hamachi_get_drvinfo, - .nway_reset = hamachi_nway_reset, - .get_link = hamachi_get_link, - .get_link_ksettings = hamachi_get_link_ksettings, - .set_link_ksettings = hamachi_set_link_ksettings, -}; - -static const struct ethtool_ops ethtool_ops_no_mii = { - .begin = check_if_running, - .get_drvinfo = hamachi_get_drvinfo, -}; - -/* private ioctl: set rx,tx intr params */ -static int hamachi_siocdevprivate(struct net_device *dev, struct ifreq *rq, - void __user *data, int cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - u32 *d = (u32 *)&rq->ifr_ifru; - - if (!netif_running(dev)) - return -EINVAL; - - if (cmd != SIOCDEVPRIVATE + 3) - return -EOPNOTSUPP; - - /* Should add this check here or an ordinary user can do nasty - * things. -KDU - * - * TODO: Shut down the Rx and Tx engines while doing this. - */ - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - writel(d[0], np->base + TxIntrCtrl); - writel(d[1], np->base + RxIntrCtrl); - printk(KERN_NOTICE "%s: tx %08x, rx %08x intr\n", dev->name, - (u32)readl(np->base + TxIntrCtrl), - (u32)readl(np->base + RxIntrCtrl)); - - return 0; -} - -static int hamachi_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct hamachi_private *np = netdev_priv(dev); - struct mii_ioctl_data *data = if_mii(rq); - int rc; - - if (!netif_running(dev)) - return -EINVAL; - - spin_lock_irq(&np->lock); - rc = generic_mii_ioctl(&np->mii_if, data, cmd, NULL); - spin_unlock_irq(&np->lock); - - return rc; -} - - -static void hamachi_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - if (dev) { - struct hamachi_private *hmp = netdev_priv(dev); - - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, hmp->rx_ring, - hmp->rx_ring_dma); - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, hmp->tx_ring, - hmp->tx_ring_dma); - unregister_netdev(dev); - iounmap(hmp->base); - free_netdev(dev); - pci_release_regions(pdev); - } -} - -static const struct pci_device_id hamachi_pci_tbl[] = { - { 0x1318, 0x0911, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, hamachi_pci_tbl); - -static struct pci_driver hamachi_driver = { - .name = DRV_NAME, - .id_table = hamachi_pci_tbl, - .probe = hamachi_init_one, - .remove = hamachi_remove_one, -}; - -static int __init hamachi_init (void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_register_driver(&hamachi_driver); -} - -static void __exit hamachi_exit (void) -{ - pci_unregister_driver(&hamachi_driver); -} - - -module_init(hamachi_init); -module_exit(hamachi_exit); diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c deleted file mode 100644 index 1e25ac13a7d8..000000000000 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ /dev/null @@ -1,1438 +0,0 @@ -/* yellowfin.c: A Packet Engines G-NIC ethernet driver for linux. */ -/* - Written 1997-2001 by Donald Becker. - - This software may be used and distributed according to the terms of - the GNU General Public License (GPL), incorporated herein by reference. - Drivers based on or derived from this code fall under the GPL and must - retain the authorship, copyright and license notice. This file is not - a complete program and may only be used when the entire operating - system is licensed under the GPL. - - This driver is for the Packet Engines G-NIC PCI Gigabit Ethernet adapter. - It also supports the Symbios Logic version of the same chip core. - - The author may be reached as becker@scyld.com, or C/O - Scyld Computing Corporation - 410 Severn Ave., Suite 210 - Annapolis MD 21403 - - Support and updates available at - http://www.scyld.com/network/yellowfin.html - [link no longer provides useful info -jgarzik] - -*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#define DRV_NAME "yellowfin" -#define DRV_VERSION "2.1" -#define DRV_RELDATE "Sep 11, 2006" - -/* The user-configurable values. - These may be modified when a driver module is loaded.*/ - -static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static int max_interrupt_work = 20; -static int mtu; -#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ -/* System-wide count of bogus-rx frames. */ -static int bogus_rx; -static int dma_ctrl = 0x004A0263; /* Constrained by errata */ -static int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ -#elif defined(YF_NEW) /* A future perfect board :->. */ -static int dma_ctrl = 0x00CAC277; /* Override when loading module! */ -static int fifo_cfg = 0x0028; -#else -static const int dma_ctrl = 0x004A0263; /* Constrained by errata */ -static const int fifo_cfg = 0x0020; /* Bypass external Tx FIFO. */ -#endif - -/* Set the copy breakpoint for the copy-only-tiny-frames scheme. - Setting to > 1514 effectively disables this feature. */ -static int rx_copybreak; - -/* Used to pass the media type, etc. - No media types are currently defined. These exist for driver - interoperability. -*/ -#define MAX_UNITS 8 /* More are supported, limit only on options */ -static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; -static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; - -/* Do ugly workaround for GX server chipset errata. */ -static int gx_fix; - -/* Operational parameters that are set at compile time. */ - -/* Keep the ring sizes a power of two for efficiency. - Making the Tx ring too long decreases the effectiveness of channel - bonding and packet priority. - There are no ill effects from too-large receive rings. */ -#define TX_RING_SIZE 16 -#define TX_QUEUE_SIZE 12 /* Must be > 4 && <= TX_RING_SIZE */ -#define RX_RING_SIZE 64 -#define STATUS_TOTAL_SIZE TX_RING_SIZE*sizeof(struct tx_status_words) -#define TX_TOTAL_SIZE 2*TX_RING_SIZE*sizeof(struct yellowfin_desc) -#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct yellowfin_desc) - -/* Operational parameters that usually are not changed. */ -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (2*HZ) -#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ - -#define yellowfin_debug debug - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/interrupt.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/mii.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/ethtool.h> -#include <linux/crc32.h> -#include <linux/bitops.h> -#include <linux/uaccess.h> -#include <asm/processor.h> /* Processor type for cache alignment. */ -#include <linux/unaligned.h> -#include <asm/io.h> - -/* These identify the driver base version and may not be removed. */ -static const char version[] = - KERN_INFO DRV_NAME ".c:v1.05 1/09/2001 Written by Donald Becker <becker@scyld.com>\n" - " (unofficial 2.4.x port, " DRV_VERSION ", " DRV_RELDATE ")\n"; - -MODULE_AUTHOR("Donald Becker <becker@scyld.com>"); -MODULE_DESCRIPTION("Packet Engines Yellowfin G-NIC Gigabit Ethernet driver"); -MODULE_LICENSE("GPL"); - -module_param(max_interrupt_work, int, 0); -module_param(mtu, int, 0); -module_param(debug, int, 0); -module_param(rx_copybreak, int, 0); -module_param_array(options, int, NULL, 0); -module_param_array(full_duplex, int, NULL, 0); -module_param(gx_fix, int, 0); -MODULE_PARM_DESC(max_interrupt_work, "G-NIC maximum events handled per interrupt"); -MODULE_PARM_DESC(mtu, "G-NIC MTU (all boards)"); -MODULE_PARM_DESC(debug, "G-NIC debug level (0-7)"); -MODULE_PARM_DESC(rx_copybreak, "G-NIC copy breakpoint for copy-only-tiny-frames"); -MODULE_PARM_DESC(options, "G-NIC: Bits 0-3: media type, bit 17: full duplex"); -MODULE_PARM_DESC(full_duplex, "G-NIC full duplex setting(s) (1)"); -MODULE_PARM_DESC(gx_fix, "G-NIC: enable GX server chipset bug workaround (0-1)"); - -/* - Theory of Operation - -I. Board Compatibility - -This device driver is designed for the Packet Engines "Yellowfin" Gigabit -Ethernet adapter. The G-NIC 64-bit PCI card is supported, as well as the -Symbios 53C885E dual function chip. - -II. Board-specific settings - -PCI bus devices are configured by the system at boot time, so no jumpers -need to be set on the board. The system BIOS preferably should assign the -PCI INTA signal to an otherwise unused system IRQ line. -Note: Kernel versions earlier than 1.3.73 do not support shared PCI -interrupt lines. - -III. Driver operation - -IIIa. Ring buffers - -The Yellowfin uses the Descriptor Based DMA Architecture specified by Apple. -This is a descriptor list scheme similar to that used by the EEPro100 and -Tulip. This driver uses two statically allocated fixed-size descriptor lists -formed into rings by a branch from the final descriptor to the beginning of -the list. The ring sizes are set at compile time by RX/TX_RING_SIZE. - -The driver allocates full frame size skbuffs for the Rx ring buffers at -open() time and passes the skb->data field to the Yellowfin as receive data -buffers. When an incoming frame is less than RX_COPYBREAK bytes long, -a fresh skbuff is allocated and the frame is copied to the new skbuff. -When the incoming frame is larger, the skbuff is passed directly up the -protocol stack and replaced by a newly allocated skbuff. - -The RX_COPYBREAK value is chosen to trade-off the memory wasted by -using a full-sized skbuff for small frames vs. the copying costs of larger -frames. For small frames the copying cost is negligible (esp. considering -that we are pre-loading the cache with immediately useful header -information). For large frames the copying cost is non-trivial, and the -larger copy might flush the cache of useful data. - -IIIC. Synchronization - -The driver runs as two independent, single-threaded flows of control. One -is the send-packet routine, which enforces single-threaded use by the -dev->tbusy flag. The other thread is the interrupt handler, which is single -threaded by the hardware and other software. - -The send packet thread has partial control over the Tx ring and 'dev->tbusy' -flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next -queue slot is empty, it clears the tbusy flag when finished otherwise it sets -the 'yp->tx_full' flag. - -The interrupt handler has exclusive control over the Rx ring and records stats -from the Tx ring. After reaping the stats, it marks the Tx queue entry as -empty by incrementing the dirty_tx mark. Iff the 'yp->tx_full' flag is set, it -clears both the tx_full and tbusy flags. - -IV. Notes - -Thanks to Kim Stearns of Packet Engines for providing a pair of G-NIC boards. -Thanks to Bruce Faust of Digitalscape for providing both their SYM53C885 board -and an AlphaStation to verify the Alpha port! - -IVb. References - -Yellowfin Engineering Design Specification, 4/23/97 Preliminary/Confidential -Symbios SYM53C885 PCI-SCSI/Fast Ethernet Multifunction Controller Preliminary - Data Manual v3.0 -http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html -http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html - -IVc. Errata - -See Packet Engines confidential appendix (prototype chips only). -*/ - - - -enum capability_flags { - HasMII=1, FullTxStatus=2, IsGigabit=4, HasMulticastBug=8, FullRxStatus=16, - HasMACAddrBug=32, /* Only on early revs. */ - DontUseEeprom=64, /* Don't read the MAC from the EEPROm. */ -}; - -/* The PCI I/O space extent. */ -enum { - YELLOWFIN_SIZE = 0x100, -}; - -struct pci_id_info { - const char *name; - struct match_info { - int pci, pci_mask, subsystem, subsystem_mask; - int revision, revision_mask; /* Only 8 bits. */ - } id; - int drv_flags; /* Driver use, intended as capability flags. */ -}; - -static const struct pci_id_info pci_id_tbl[] = { - {"Yellowfin G-NIC Gigabit Ethernet", { 0x07021000, 0xffffffff}, - FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug | DontUseEeprom}, - {"Symbios SYM83C885", { 0x07011000, 0xffffffff}, - HasMII | DontUseEeprom }, - { } -}; - -static const struct pci_device_id yellowfin_pci_tbl[] = { - { 0x1000, 0x0702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0x1000, 0x0701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, - { } -}; -MODULE_DEVICE_TABLE (pci, yellowfin_pci_tbl); - - -/* Offsets to the Yellowfin registers. Various sizes and alignments. */ -enum yellowfin_offsets { - TxCtrl=0x00, TxStatus=0x04, TxPtr=0x0C, - TxIntrSel=0x10, TxBranchSel=0x14, TxWaitSel=0x18, - RxCtrl=0x40, RxStatus=0x44, RxPtr=0x4C, - RxIntrSel=0x50, RxBranchSel=0x54, RxWaitSel=0x58, - EventStatus=0x80, IntrEnb=0x82, IntrClear=0x84, IntrStatus=0x86, - ChipRev=0x8C, DMACtrl=0x90, TxThreshold=0x94, - Cnfg=0xA0, FrameGap0=0xA2, FrameGap1=0xA4, - MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC, - MII_Status=0xAE, - RxDepth=0xB8, FlowCtrl=0xBC, - AddrMode=0xD0, StnAddr=0xD2, HashTbl=0xD8, FIFOcfg=0xF8, - EEStatus=0xF0, EECtrl=0xF1, EEAddr=0xF2, EERead=0xF3, EEWrite=0xF4, - EEFeature=0xF5, -}; - -/* The Yellowfin Rx and Tx buffer descriptors. - Elements are written as 32 bit for endian portability. */ -struct yellowfin_desc { - __le32 dbdma_cmd; - __le32 addr; - __le32 branch_addr; - __le32 result_status; -}; - -struct tx_status_words { -#ifdef __BIG_ENDIAN - u16 tx_errs; - u16 tx_cnt; - u16 paused; - u16 total_tx_cnt; -#else /* Little endian chips. */ - u16 tx_cnt; - u16 tx_errs; - u16 total_tx_cnt; - u16 paused; -#endif /* __BIG_ENDIAN */ -}; - -/* Bits in yellowfin_desc.cmd */ -enum desc_cmd_bits { - CMD_TX_PKT=0x10000000, CMD_RX_BUF=0x20000000, CMD_TXSTATUS=0x30000000, - CMD_NOP=0x60000000, CMD_STOP=0x70000000, - BRANCH_ALWAYS=0x0C0000, INTR_ALWAYS=0x300000, WAIT_ALWAYS=0x030000, - BRANCH_IFTRUE=0x040000, -}; - -/* Bits in yellowfin_desc.status */ -enum desc_status_bits { RX_EOP=0x0040, }; - -/* Bits in the interrupt status/mask registers. */ -enum intr_status_bits { - IntrRxDone=0x01, IntrRxInvalid=0x02, IntrRxPCIFault=0x04,IntrRxPCIErr=0x08, - IntrTxDone=0x10, IntrTxInvalid=0x20, IntrTxPCIFault=0x40,IntrTxPCIErr=0x80, - IntrEarlyRx=0x100, IntrWakeup=0x200, }; - -#define PRIV_ALIGN 31 /* Required alignment mask */ -#define MII_CNT 4 -struct yellowfin_private { - /* Descriptor rings first for alignment. - Tx requires a second descriptor for status. */ - struct yellowfin_desc *rx_ring; - struct yellowfin_desc *tx_ring; - struct sk_buff* rx_skbuff[RX_RING_SIZE]; - struct sk_buff* tx_skbuff[TX_RING_SIZE]; - dma_addr_t rx_ring_dma; - dma_addr_t tx_ring_dma; - - struct tx_status_words *tx_status; - dma_addr_t tx_status_dma; - - struct timer_list timer; /* Media selection timer. */ - /* Frequently used and paired value: keep adjacent for cache effect. */ - int chip_id, drv_flags; - struct pci_dev *pci_dev; - unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ - unsigned int rx_buf_sz; /* Based on MTU+slack. */ - struct tx_status_words *tx_tail_desc; - unsigned int cur_tx, dirty_tx; - int tx_threshold; - unsigned int tx_full:1; /* The Tx queue is full. */ - unsigned int full_duplex:1; /* Full-duplex operation requested. */ - unsigned int duplex_lock:1; - unsigned int medialock:1; /* Do not sense media. */ - unsigned int default_port:4; /* Last dev->if_port value. */ - /* MII transceiver section. */ - int mii_cnt; /* MII device addresses. */ - u16 advertising; /* NWay media advertisement */ - unsigned char phys[MII_CNT]; /* MII device addresses, only first one used */ - spinlock_t lock; - void __iomem *base; -}; - -static int read_eeprom(void __iomem *ioaddr, int location); -static int mdio_read(void __iomem *ioaddr, int phy_id, int location); -static void mdio_write(void __iomem *ioaddr, int phy_id, int location, int value); -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static int yellowfin_open(struct net_device *dev); -static void yellowfin_timer(struct timer_list *t); -static void yellowfin_tx_timeout(struct net_device *dev, unsigned int txqueue); -static int yellowfin_init_ring(struct net_device *dev); -static netdev_tx_t yellowfin_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance); -static int yellowfin_rx(struct net_device *dev); -static void yellowfin_error(struct net_device *dev, int intr_status); -static int yellowfin_close(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; - -static const struct net_device_ops netdev_ops = { - .ndo_open = yellowfin_open, - .ndo_stop = yellowfin_close, - .ndo_start_xmit = yellowfin_start_xmit, - .ndo_set_rx_mode = set_rx_mode, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_mac_address = eth_mac_addr, - .ndo_eth_ioctl = netdev_ioctl, - .ndo_tx_timeout = yellowfin_tx_timeout, -}; - -static int yellowfin_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct net_device *dev; - struct yellowfin_private *np; - int irq; - int chip_idx = ent->driver_data; - static int find_cnt; - void __iomem *ioaddr; - int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0; - int drv_flags = pci_id_tbl[chip_idx].drv_flags; - void *ring_space; - dma_addr_t ring_dma; -#ifdef USE_IO_OPS - int bar = 0; -#else - int bar = 1; -#endif - u8 addr[ETH_ALEN]; - -/* when built into the kernel, we only print version if device is found */ -#ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(version); -#endif - - i = pci_enable_device(pdev); - if (i) return i; - - dev = alloc_etherdev(sizeof(*np)); - if (!dev) - return -ENOMEM; - - SET_NETDEV_DEV(dev, &pdev->dev); - - np = netdev_priv(dev); - - if (pci_request_regions(pdev, DRV_NAME)) - goto err_out_free_netdev; - - pci_set_master (pdev); - - ioaddr = pci_iomap(pdev, bar, YELLOWFIN_SIZE); - if (!ioaddr) - goto err_out_free_res; - - irq = pdev->irq; - - if (drv_flags & DontUseEeprom) - for (i = 0; i < 6; i++) - addr[i] = ioread8(ioaddr + StnAddr + i); - else { - int ee_offset = (read_eeprom(ioaddr, 6) == 0xff ? 0x100 : 0); - for (i = 0; i < 6; i++) - addr[i] = read_eeprom(ioaddr, ee_offset + i); - } - eth_hw_addr_set(dev, addr); - - /* Reset the chip. */ - iowrite32(0x80000000, ioaddr + DMACtrl); - - pci_set_drvdata(pdev, dev); - spin_lock_init(&np->lock); - - np->pci_dev = pdev; - np->chip_id = chip_idx; - np->drv_flags = drv_flags; - np->base = ioaddr; - - ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_cleardev; - np->tx_ring = ring_space; - np->tx_ring_dma = ring_dma; - - ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma, - GFP_KERNEL); - if (!ring_space) - goto err_out_unmap_tx; - np->rx_ring = ring_space; - np->rx_ring_dma = ring_dma; - - ring_space = dma_alloc_coherent(&pdev->dev, STATUS_TOTAL_SIZE, - &ring_dma, GFP_KERNEL); - if (!ring_space) - goto err_out_unmap_rx; - np->tx_status = ring_space; - np->tx_status_dma = ring_dma; - - if (dev->mem_start) - option = dev->mem_start; - - /* The lower four bits are the media type. */ - if (option > 0) { - if (option & 0x200) - np->full_duplex = 1; - np->default_port = option & 15; - if (np->default_port) - np->medialock = 1; - } - if (find_cnt < MAX_UNITS && full_duplex[find_cnt] > 0) - np->full_duplex = 1; - - if (np->full_duplex) - np->duplex_lock = 1; - - /* The Yellowfin-specific entries in the device structure. */ - dev->netdev_ops = &netdev_ops; - dev->ethtool_ops = ðtool_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - if (mtu) - dev->mtu = mtu; - - i = register_netdev(dev); - if (i) - goto err_out_unmap_status; - - netdev_info(dev, "%s type %8x at %p, %pM, IRQ %d\n", - pci_id_tbl[chip_idx].name, - ioread32(ioaddr + ChipRev), ioaddr, - dev->dev_addr, irq); - - if (np->drv_flags & HasMII) { - int phy, phy_idx = 0; - for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) { - int mii_status = mdio_read(ioaddr, phy, 1); - if (mii_status != 0xffff && mii_status != 0x0000) { - np->phys[phy_idx++] = phy; - np->advertising = mdio_read(ioaddr, phy, 4); - netdev_info(dev, "MII PHY found at address %d, status 0x%04x advertising %04x\n", - phy, mii_status, np->advertising); - } - } - np->mii_cnt = phy_idx; - } - - find_cnt++; - - return 0; - -err_out_unmap_status: - dma_free_coherent(&pdev->dev, STATUS_TOTAL_SIZE, np->tx_status, - np->tx_status_dma); -err_out_unmap_rx: - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, - np->rx_ring_dma); -err_out_unmap_tx: - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, - np->tx_ring_dma); -err_out_cleardev: - pci_iounmap(pdev, ioaddr); -err_out_free_res: - pci_release_regions(pdev); -err_out_free_netdev: - free_netdev (dev); - return -ENODEV; -} - -static int read_eeprom(void __iomem *ioaddr, int location) -{ - int bogus_cnt = 10000; /* Typical 33Mhz: 1050 ticks */ - - iowrite8(location, ioaddr + EEAddr); - iowrite8(0x30 | ((location >> 8) & 7), ioaddr + EECtrl); - while ((ioread8(ioaddr + EEStatus) & 0x80) && --bogus_cnt > 0) - ; - return ioread8(ioaddr + EERead); -} - -/* MII Managemen Data I/O accesses. - These routines assume the MDIO controller is idle, and do not exit until - the command is finished. */ - -static int mdio_read(void __iomem *ioaddr, int phy_id, int location) -{ - int i; - - iowrite16((phy_id<<8) + location, ioaddr + MII_Addr); - iowrite16(1, ioaddr + MII_Cmd); - for (i = 10000; i >= 0; i--) - if ((ioread16(ioaddr + MII_Status) & 1) == 0) - break; - return ioread16(ioaddr + MII_Rd_Data); -} - -static void mdio_write(void __iomem *ioaddr, int phy_id, int location, int value) -{ - int i; - - iowrite16((phy_id<<8) + location, ioaddr + MII_Addr); - iowrite16(value, ioaddr + MII_Wr_Data); - - /* Wait for the command to finish. */ - for (i = 10000; i >= 0; i--) - if ((ioread16(ioaddr + MII_Status) & 1) == 0) - break; -} - - -static int yellowfin_open(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - const int irq = yp->pci_dev->irq; - void __iomem *ioaddr = yp->base; - int i, rc; - - /* Reset the chip. */ - iowrite32(0x80000000, ioaddr + DMACtrl); - - rc = request_irq(irq, yellowfin_interrupt, IRQF_SHARED, dev->name, dev); - if (rc) - return rc; - - rc = yellowfin_init_ring(dev); - if (rc < 0) - goto err_free_irq; - - iowrite32(yp->rx_ring_dma, ioaddr + RxPtr); - iowrite32(yp->tx_ring_dma, ioaddr + TxPtr); - - for (i = 0; i < 6; i++) - iowrite8(dev->dev_addr[i], ioaddr + StnAddr + i); - - /* Set up various condition 'select' registers. - There are no options here. */ - iowrite32(0x00800080, ioaddr + TxIntrSel); /* Interrupt on Tx abort */ - iowrite32(0x00800080, ioaddr + TxBranchSel); /* Branch on Tx abort */ - iowrite32(0x00400040, ioaddr + TxWaitSel); /* Wait on Tx status */ - iowrite32(0x00400040, ioaddr + RxIntrSel); /* Interrupt on Rx done */ - iowrite32(0x00400040, ioaddr + RxBranchSel); /* Branch on Rx error */ - iowrite32(0x00400040, ioaddr + RxWaitSel); /* Wait on Rx done */ - - /* Initialize other registers: with so many this eventually this will - converted to an offset/value list. */ - iowrite32(dma_ctrl, ioaddr + DMACtrl); - iowrite16(fifo_cfg, ioaddr + FIFOcfg); - /* Enable automatic generation of flow control frames, period 0xffff. */ - iowrite32(0x0030FFFF, ioaddr + FlowCtrl); - - yp->tx_threshold = 32; - iowrite32(yp->tx_threshold, ioaddr + TxThreshold); - - if (dev->if_port == 0) - dev->if_port = yp->default_port; - - netif_start_queue(dev); - - /* Setting the Rx mode will start the Rx process. */ - if (yp->drv_flags & IsGigabit) { - /* We are always in full-duplex mode with gigabit! */ - yp->full_duplex = 1; - iowrite16(0x01CF, ioaddr + Cnfg); - } else { - iowrite16(0x0018, ioaddr + FrameGap0); /* 0060/4060 for non-MII 10baseT */ - iowrite16(0x1018, ioaddr + FrameGap1); - iowrite16(0x101C | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg); - } - set_rx_mode(dev); - - /* Enable interrupts by setting the interrupt mask. */ - iowrite16(0x81ff, ioaddr + IntrEnb); /* See enum intr_status_bits */ - iowrite16(0x0000, ioaddr + EventStatus); /* Clear non-interrupting events */ - iowrite32(0x80008000, ioaddr + RxCtrl); /* Start Rx and Tx channels. */ - iowrite32(0x80008000, ioaddr + TxCtrl); - - if (yellowfin_debug > 2) { - netdev_printk(KERN_DEBUG, dev, "Done %s()\n", __func__); - } - - /* Set the timer to check for link beat. */ - timer_setup(&yp->timer, yellowfin_timer, 0); - yp->timer.expires = jiffies + 3*HZ; - add_timer(&yp->timer); -out: - return rc; - -err_free_irq: - free_irq(irq, dev); - goto out; -} - -static void yellowfin_timer(struct timer_list *t) -{ - struct yellowfin_private *yp = timer_container_of(yp, t, timer); - struct net_device *dev = pci_get_drvdata(yp->pci_dev); - void __iomem *ioaddr = yp->base; - int next_tick = 60*HZ; - - if (yellowfin_debug > 3) { - netdev_printk(KERN_DEBUG, dev, "Yellowfin timer tick, status %08x\n", - ioread16(ioaddr + IntrStatus)); - } - - if (yp->mii_cnt) { - int bmsr = mdio_read(ioaddr, yp->phys[0], MII_BMSR); - int lpa = mdio_read(ioaddr, yp->phys[0], MII_LPA); - int negotiated = lpa & yp->advertising; - if (yellowfin_debug > 1) - netdev_printk(KERN_DEBUG, dev, "MII #%d status register is %04x, link partner capability %04x\n", - yp->phys[0], bmsr, lpa); - - yp->full_duplex = mii_duplex(yp->duplex_lock, negotiated); - - iowrite16(0x101C | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg); - - if (bmsr & BMSR_LSTATUS) - next_tick = 60*HZ; - else - next_tick = 3*HZ; - } - - yp->timer.expires = jiffies + next_tick; - add_timer(&yp->timer); -} - -static void yellowfin_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct yellowfin_private *yp = netdev_priv(dev); - void __iomem *ioaddr = yp->base; - - netdev_warn(dev, "Yellowfin transmit timed out at %d/%d Tx status %04x, Rx status %04x, resetting...\n", - yp->cur_tx, yp->dirty_tx, - ioread32(ioaddr + TxStatus), - ioread32(ioaddr + RxStatus)); - - /* Note: these should be KERN_DEBUG. */ - if (yellowfin_debug) { - int i; - pr_warn(" Rx ring %p: ", yp->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - pr_cont(" %08x", yp->rx_ring[i].result_status); - pr_cont("\n"); - pr_warn(" Tx ring %p: ", yp->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - pr_cont(" %04x /%08x", - yp->tx_status[i].tx_errs, - yp->tx_ring[i].result_status); - pr_cont("\n"); - } - - /* If the hardware is found to hang regularly, we will update the code - to reinitialize the chip here. */ - dev->if_port = 0; - - /* Wake the potentially-idle transmit channel. */ - iowrite32(0x10001000, yp->base + TxCtrl); - if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE) - netif_wake_queue (dev); /* Typical path */ - - netif_trans_update(dev); /* prevent tx timeout */ - dev->stats.tx_errors++; -} - -/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ -static int yellowfin_init_ring(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - int i, j; - - yp->tx_full = 0; - yp->cur_rx = yp->cur_tx = 0; - yp->dirty_tx = 0; - - yp->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); - - for (i = 0; i < RX_RING_SIZE; i++) { - yp->rx_ring[i].dbdma_cmd = - cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz); - yp->rx_ring[i].branch_addr = cpu_to_le32(yp->rx_ring_dma + - ((i+1)%RX_RING_SIZE)*sizeof(struct yellowfin_desc)); - } - - for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *skb = netdev_alloc_skb(dev, yp->rx_buf_sz + 2); - yp->rx_skbuff[i] = skb; - if (skb == NULL) - break; - skb_reserve(skb, 2); /* 16 byte align the IP header. */ - yp->rx_ring[i].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - yp->rx_buf_sz, - DMA_FROM_DEVICE)); - } - if (i != RX_RING_SIZE) { - for (j = 0; j < i; j++) - dev_kfree_skb(yp->rx_skbuff[j]); - return -ENOMEM; - } - yp->rx_ring[i-1].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); - -#define NO_TXSTATS -#ifdef NO_TXSTATS - /* In this mode the Tx ring needs only a single descriptor. */ - for (i = 0; i < TX_RING_SIZE; i++) { - yp->tx_skbuff[i] = NULL; - yp->tx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[i].branch_addr = cpu_to_le32(yp->tx_ring_dma + - ((i+1)%TX_RING_SIZE)*sizeof(struct yellowfin_desc)); - } - /* Wrap ring */ - yp->tx_ring[--i].dbdma_cmd = cpu_to_le32(CMD_STOP | BRANCH_ALWAYS); -#else -{ - /* Tx ring needs a pair of descriptors, the second for the status. */ - for (i = 0; i < TX_RING_SIZE; i++) { - j = 2*i; - yp->tx_skbuff[i] = 0; - /* Branch on Tx error. */ - yp->tx_ring[j].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[j].branch_addr = cpu_to_le32(yp->tx_ring_dma + - (j+1)*sizeof(struct yellowfin_desc)); - j++; - if (yp->flags & FullTxStatus) { - yp->tx_ring[j].dbdma_cmd = - cpu_to_le32(CMD_TXSTATUS | sizeof(*yp->tx_status)); - yp->tx_ring[j].request_cnt = sizeof(*yp->tx_status); - yp->tx_ring[j].addr = cpu_to_le32(yp->tx_status_dma + - i*sizeof(struct tx_status_words)); - } else { - /* Symbios chips write only tx_errs word. */ - yp->tx_ring[j].dbdma_cmd = - cpu_to_le32(CMD_TXSTATUS | INTR_ALWAYS | 2); - yp->tx_ring[j].request_cnt = 2; - /* Om pade ummmmm... */ - yp->tx_ring[j].addr = cpu_to_le32(yp->tx_status_dma + - i*sizeof(struct tx_status_words) + - &(yp->tx_status[0].tx_errs) - - &(yp->tx_status[0])); - } - yp->tx_ring[j].branch_addr = cpu_to_le32(yp->tx_ring_dma + - ((j+1)%(2*TX_RING_SIZE))*sizeof(struct yellowfin_desc)); - } - /* Wrap ring */ - yp->tx_ring[++j].dbdma_cmd |= cpu_to_le32(BRANCH_ALWAYS | INTR_ALWAYS); -} -#endif - yp->tx_tail_desc = &yp->tx_status[0]; - return 0; -} - -static netdev_tx_t yellowfin_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - unsigned entry; - int len = skb->len; - - netif_stop_queue (dev); - - /* Note: Ordering is important here, set the field with the - "ownership" bit last, and only then increment cur_tx. */ - - /* Calculate the next Tx descriptor entry. */ - entry = yp->cur_tx % TX_RING_SIZE; - - if (gx_fix) { /* Note: only works for paddable protocols e.g. IP. */ - int cacheline_end = ((unsigned long)skb->data + skb->len) % 32; - /* Fix GX chipset errata. */ - if (cacheline_end > 24 || cacheline_end == 0) { - len = skb->len + 32 - cacheline_end + 1; - if (skb_padto(skb, len)) { - yp->tx_skbuff[entry] = NULL; - netif_wake_queue(dev); - return NETDEV_TX_OK; - } - } - } - yp->tx_skbuff[entry] = skb; - -#ifdef NO_TXSTATS - yp->tx_ring[entry].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - len, DMA_TO_DEVICE)); - yp->tx_ring[entry].result_status = 0; - if (entry >= TX_RING_SIZE-1) { - /* New stop command. */ - yp->tx_ring[0].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[TX_RING_SIZE-1].dbdma_cmd = - cpu_to_le32(CMD_TX_PKT|BRANCH_ALWAYS | len); - } else { - yp->tx_ring[entry+1].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->tx_ring[entry].dbdma_cmd = - cpu_to_le32(CMD_TX_PKT | BRANCH_IFTRUE | len); - } - yp->cur_tx++; -#else - yp->tx_ring[entry<<1].request_cnt = len; - yp->tx_ring[entry<<1].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - len, DMA_TO_DEVICE)); - /* The input_last (status-write) command is constant, but we must - rewrite the subsequent 'stop' command. */ - - yp->cur_tx++; - { - unsigned next_entry = yp->cur_tx % TX_RING_SIZE; - yp->tx_ring[next_entry<<1].dbdma_cmd = cpu_to_le32(CMD_STOP); - } - /* Final step -- overwrite the old 'stop' command. */ - - yp->tx_ring[entry<<1].dbdma_cmd = - cpu_to_le32( ((entry % 6) == 0 ? CMD_TX_PKT|INTR_ALWAYS|BRANCH_IFTRUE : - CMD_TX_PKT | BRANCH_IFTRUE) | len); -#endif - - /* Non-x86 Todo: explicitly flush cache lines here. */ - - /* Wake the potentially-idle transmit channel. */ - iowrite32(0x10001000, yp->base + TxCtrl); - - if (yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE) - netif_start_queue (dev); /* Typical path */ - else - yp->tx_full = 1; - - if (yellowfin_debug > 4) { - netdev_printk(KERN_DEBUG, dev, "Yellowfin transmit frame #%d queued in slot %d\n", - yp->cur_tx, entry); - } - return NETDEV_TX_OK; -} - -/* The interrupt handler does all of the Rx thread work and cleans up - after the Tx thread. */ -static irqreturn_t yellowfin_interrupt(int irq, void *dev_instance) -{ - struct net_device *dev = dev_instance; - struct yellowfin_private *yp; - void __iomem *ioaddr; - int boguscnt = max_interrupt_work; - unsigned int handled = 0; - - yp = netdev_priv(dev); - ioaddr = yp->base; - - spin_lock (&yp->lock); - - do { - u16 intr_status = ioread16(ioaddr + IntrClear); - - if (yellowfin_debug > 4) - netdev_printk(KERN_DEBUG, dev, "Yellowfin interrupt, status %04x\n", - intr_status); - - if (intr_status == 0) - break; - handled = 1; - - if (intr_status & (IntrRxDone | IntrEarlyRx)) { - yellowfin_rx(dev); - iowrite32(0x10001000, ioaddr + RxCtrl); /* Wake Rx engine. */ - } - -#ifdef NO_TXSTATS - for (; yp->cur_tx - yp->dirty_tx > 0; yp->dirty_tx++) { - int entry = yp->dirty_tx % TX_RING_SIZE; - struct sk_buff *skb; - - if (yp->tx_ring[entry].result_status == 0) - break; - skb = yp->tx_skbuff[entry]; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - /* Free the original skb. */ - dma_unmap_single(&yp->pci_dev->dev, - le32_to_cpu(yp->tx_ring[entry].addr), - skb->len, DMA_TO_DEVICE); - dev_consume_skb_irq(skb); - yp->tx_skbuff[entry] = NULL; - } - if (yp->tx_full && - yp->cur_tx - yp->dirty_tx < TX_QUEUE_SIZE - 4) { - /* The ring is no longer full, clear tbusy. */ - yp->tx_full = 0; - netif_wake_queue(dev); - } -#else - if ((intr_status & IntrTxDone) || (yp->tx_tail_desc->tx_errs)) { - unsigned dirty_tx = yp->dirty_tx; - - for (dirty_tx = yp->dirty_tx; yp->cur_tx - dirty_tx > 0; - dirty_tx++) { - /* Todo: optimize this. */ - int entry = dirty_tx % TX_RING_SIZE; - u16 tx_errs = yp->tx_status[entry].tx_errs; - struct sk_buff *skb; - -#ifndef final_version - if (yellowfin_debug > 5) - netdev_printk(KERN_DEBUG, dev, "Tx queue %d check, Tx status %04x %04x %04x %04x\n", - entry, - yp->tx_status[entry].tx_cnt, - yp->tx_status[entry].tx_errs, - yp->tx_status[entry].total_tx_cnt, - yp->tx_status[entry].paused); -#endif - if (tx_errs == 0) - break; /* It still hasn't been Txed */ - skb = yp->tx_skbuff[entry]; - if (tx_errs & 0xF810) { - /* There was an major error, log it. */ -#ifndef final_version - if (yellowfin_debug > 1) - netdev_printk(KERN_DEBUG, dev, "Transmit error, Tx status %04x\n", - tx_errs); -#endif - dev->stats.tx_errors++; - if (tx_errs & 0xF800) dev->stats.tx_aborted_errors++; - if (tx_errs & 0x0800) dev->stats.tx_carrier_errors++; - if (tx_errs & 0x2000) dev->stats.tx_window_errors++; - if (tx_errs & 0x8000) dev->stats.tx_fifo_errors++; - } else { -#ifndef final_version - if (yellowfin_debug > 4) - netdev_printk(KERN_DEBUG, dev, "Normal transmit, Tx status %04x\n", - tx_errs); -#endif - dev->stats.tx_bytes += skb->len; - dev->stats.collisions += tx_errs & 15; - dev->stats.tx_packets++; - } - /* Free the original skb. */ - dma_unmap_single(&yp->pci_dev->dev, - yp->tx_ring[entry << 1].addr, - skb->len, DMA_TO_DEVICE); - dev_consume_skb_irq(skb); - yp->tx_skbuff[entry] = 0; - /* Mark status as empty. */ - yp->tx_status[entry].tx_errs = 0; - } - -#ifndef final_version - if (yp->cur_tx - dirty_tx > TX_RING_SIZE) { - netdev_err(dev, "Out-of-sync dirty pointer, %d vs. %d, full=%d\n", - dirty_tx, yp->cur_tx, yp->tx_full); - dirty_tx += TX_RING_SIZE; - } -#endif - - if (yp->tx_full && - yp->cur_tx - dirty_tx < TX_QUEUE_SIZE - 2) { - /* The ring is no longer full, clear tbusy. */ - yp->tx_full = 0; - netif_wake_queue(dev); - } - - yp->dirty_tx = dirty_tx; - yp->tx_tail_desc = &yp->tx_status[dirty_tx % TX_RING_SIZE]; - } -#endif - - /* Log errors and other uncommon events. */ - if (intr_status & 0x2ee) /* Abnormal error summary. */ - yellowfin_error(dev, intr_status); - - if (--boguscnt < 0) { - netdev_warn(dev, "Too much work at interrupt, status=%#04x\n", - intr_status); - break; - } - } while (1); - - if (yellowfin_debug > 3) - netdev_printk(KERN_DEBUG, dev, "exiting interrupt, status=%#04x\n", - ioread16(ioaddr + IntrStatus)); - - spin_unlock (&yp->lock); - return IRQ_RETVAL(handled); -} - -/* This routine is logically part of the interrupt handler, but separated - for clarity and better register allocation. */ -static int yellowfin_rx(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - int entry = yp->cur_rx % RX_RING_SIZE; - int boguscnt = yp->dirty_rx + RX_RING_SIZE - yp->cur_rx; - - if (yellowfin_debug > 4) { - printk(KERN_DEBUG " In yellowfin_rx(), entry %d status %08x\n", - entry, yp->rx_ring[entry].result_status); - printk(KERN_DEBUG " #%d desc. %08x %08x %08x\n", - entry, yp->rx_ring[entry].dbdma_cmd, yp->rx_ring[entry].addr, - yp->rx_ring[entry].result_status); - } - - /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (1) { - struct yellowfin_desc *desc = &yp->rx_ring[entry]; - struct sk_buff *rx_skb = yp->rx_skbuff[entry]; - s16 frame_status; - u16 desc_status; - int data_size, __maybe_unused yf_size; - u8 *buf_addr; - - if(!desc->result_status) - break; - dma_sync_single_for_cpu(&yp->pci_dev->dev, - le32_to_cpu(desc->addr), - yp->rx_buf_sz, DMA_FROM_DEVICE); - desc_status = le32_to_cpu(desc->result_status) >> 16; - buf_addr = rx_skb->data; - data_size = (le32_to_cpu(desc->dbdma_cmd) - - le32_to_cpu(desc->result_status)) & 0xffff; - frame_status = get_unaligned_le16(&(buf_addr[data_size - 2])); - if (yellowfin_debug > 4) - printk(KERN_DEBUG " %s() status was %04x\n", - __func__, frame_status); - if (--boguscnt < 0) - break; - - yf_size = sizeof(struct yellowfin_desc); - - if ( ! (desc_status & RX_EOP)) { - if (data_size != 0) - netdev_warn(dev, "Oversized Ethernet frame spanned multiple buffers, status %04x, data_size %d!\n", - desc_status, data_size); - dev->stats.rx_length_errors++; - } else if ((yp->drv_flags & IsGigabit) && (frame_status & 0x0038)) { - /* There was a error. */ - if (yellowfin_debug > 3) - printk(KERN_DEBUG " %s() Rx error was %04x\n", - __func__, frame_status); - dev->stats.rx_errors++; - if (frame_status & 0x0060) dev->stats.rx_length_errors++; - if (frame_status & 0x0008) dev->stats.rx_frame_errors++; - if (frame_status & 0x0010) dev->stats.rx_crc_errors++; - if (frame_status < 0) dev->stats.rx_dropped++; - } else if ( !(yp->drv_flags & IsGigabit) && - ((buf_addr[data_size-1] & 0x85) || buf_addr[data_size-2] & 0xC0)) { - u8 status1 = buf_addr[data_size-2]; - u8 status2 = buf_addr[data_size-1]; - dev->stats.rx_errors++; - if (status1 & 0xC0) dev->stats.rx_length_errors++; - if (status2 & 0x03) dev->stats.rx_frame_errors++; - if (status2 & 0x04) dev->stats.rx_crc_errors++; - if (status2 & 0x80) dev->stats.rx_dropped++; -#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ - } else if ((yp->flags & HasMACAddrBug) && - !ether_addr_equal(le32_to_cpu(yp->rx_ring_dma + - entry * yf_size), - dev->dev_addr) && - !ether_addr_equal(le32_to_cpu(yp->rx_ring_dma + - entry * yf_size), - "\377\377\377\377\377\377")) { - if (bogus_rx++ == 0) - netdev_warn(dev, "Bad frame to %pM\n", - buf_addr); -#endif - } else { - struct sk_buff *skb; - int pkt_len = data_size - - (yp->chip_id ? 7 : 8 + buf_addr[data_size - 8]); - /* To verify: Yellowfin Length should omit the CRC! */ - -#ifndef final_version - if (yellowfin_debug > 4) - printk(KERN_DEBUG " %s() normal Rx pkt length %d of %d, bogus_cnt %d\n", - __func__, pkt_len, data_size, boguscnt); -#endif - /* Check if the packet is long enough to just pass up the skbuff - without copying to a properly sized skbuff. */ - if (pkt_len > rx_copybreak) { - skb_put(skb = rx_skb, pkt_len); - dma_unmap_single(&yp->pci_dev->dev, - le32_to_cpu(yp->rx_ring[entry].addr), - yp->rx_buf_sz, - DMA_FROM_DEVICE); - yp->rx_skbuff[entry] = NULL; - } else { - skb = netdev_alloc_skb(dev, pkt_len + 2); - if (skb == NULL) - break; - skb_reserve(skb, 2); /* 16 byte align the IP header */ - skb_copy_to_linear_data(skb, rx_skb->data, pkt_len); - skb_put(skb, pkt_len); - dma_sync_single_for_device(&yp->pci_dev->dev, - le32_to_cpu(desc->addr), - yp->rx_buf_sz, - DMA_FROM_DEVICE); - } - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += pkt_len; - } - entry = (++yp->cur_rx) % RX_RING_SIZE; - } - - /* Refill the Rx ring buffers. */ - for (; yp->cur_rx - yp->dirty_rx > 0; yp->dirty_rx++) { - entry = yp->dirty_rx % RX_RING_SIZE; - if (yp->rx_skbuff[entry] == NULL) { - struct sk_buff *skb = netdev_alloc_skb(dev, yp->rx_buf_sz + 2); - if (skb == NULL) - break; /* Better luck next round. */ - yp->rx_skbuff[entry] = skb; - skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - yp->rx_ring[entry].addr = cpu_to_le32(dma_map_single(&yp->pci_dev->dev, - skb->data, - yp->rx_buf_sz, - DMA_FROM_DEVICE)); - } - yp->rx_ring[entry].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->rx_ring[entry].result_status = 0; /* Clear complete bit. */ - if (entry != 0) - yp->rx_ring[entry - 1].dbdma_cmd = - cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | yp->rx_buf_sz); - else - yp->rx_ring[RX_RING_SIZE - 1].dbdma_cmd = - cpu_to_le32(CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS - | yp->rx_buf_sz); - } - - return 0; -} - -static void yellowfin_error(struct net_device *dev, int intr_status) -{ - netdev_err(dev, "Something Wicked happened! %04x\n", intr_status); - /* Hmmmmm, it's not clear what to do here. */ - if (intr_status & (IntrTxPCIErr | IntrTxPCIFault)) - dev->stats.tx_errors++; - if (intr_status & (IntrRxPCIErr | IntrRxPCIFault)) - dev->stats.rx_errors++; -} - -static int yellowfin_close(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - void __iomem *ioaddr = yp->base; - int i; - - netif_stop_queue (dev); - - if (yellowfin_debug > 1) { - netdev_printk(KERN_DEBUG, dev, "Shutting down ethercard, status was Tx %04x Rx %04x Int %02x\n", - ioread16(ioaddr + TxStatus), - ioread16(ioaddr + RxStatus), - ioread16(ioaddr + IntrStatus)); - netdev_printk(KERN_DEBUG, dev, "Queue pointers were Tx %d / %d, Rx %d / %d\n", - yp->cur_tx, yp->dirty_tx, - yp->cur_rx, yp->dirty_rx); - } - - /* Disable interrupts by clearing the interrupt mask. */ - iowrite16(0x0000, ioaddr + IntrEnb); - - /* Stop the chip's Tx and Rx processes. */ - iowrite32(0x80000000, ioaddr + RxCtrl); - iowrite32(0x80000000, ioaddr + TxCtrl); - - timer_delete(&yp->timer); - -#if defined(__i386__) - if (yellowfin_debug > 2) { - printk(KERN_DEBUG " Tx ring at %08llx:\n", - (unsigned long long)yp->tx_ring_dma); - for (i = 0; i < TX_RING_SIZE*2; i++) - printk(KERN_DEBUG " %c #%d desc. %08x %08x %08x %08x\n", - ioread32(ioaddr + TxPtr) == (long)&yp->tx_ring[i] ? '>' : ' ', - i, yp->tx_ring[i].dbdma_cmd, yp->tx_ring[i].addr, - yp->tx_ring[i].branch_addr, yp->tx_ring[i].result_status); - printk(KERN_DEBUG " Tx status %p:\n", yp->tx_status); - for (i = 0; i < TX_RING_SIZE; i++) - printk(KERN_DEBUG " #%d status %04x %04x %04x %04x\n", - i, yp->tx_status[i].tx_cnt, yp->tx_status[i].tx_errs, - yp->tx_status[i].total_tx_cnt, yp->tx_status[i].paused); - - printk(KERN_DEBUG " Rx ring %08llx:\n", - (unsigned long long)yp->rx_ring_dma); - for (i = 0; i < RX_RING_SIZE; i++) { - printk(KERN_DEBUG " %c #%d desc. %08x %08x %08x\n", - ioread32(ioaddr + RxPtr) == (long)&yp->rx_ring[i] ? '>' : ' ', - i, yp->rx_ring[i].dbdma_cmd, yp->rx_ring[i].addr, - yp->rx_ring[i].result_status); - if (yellowfin_debug > 6) { - if (get_unaligned((u8*)yp->rx_ring[i].addr) != 0x69) { - int j; - - printk(KERN_DEBUG); - for (j = 0; j < 0x50; j++) - pr_cont(" %04x", - get_unaligned(((u16*)yp->rx_ring[i].addr) + j)); - pr_cont("\n"); - } - } - } - } -#endif /* __i386__ debugging only */ - - free_irq(yp->pci_dev->irq, dev); - - /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { - yp->rx_ring[i].dbdma_cmd = cpu_to_le32(CMD_STOP); - yp->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */ - if (yp->rx_skbuff[i]) { - dev_kfree_skb(yp->rx_skbuff[i]); - } - yp->rx_skbuff[i] = NULL; - } - for (i = 0; i < TX_RING_SIZE; i++) { - dev_kfree_skb(yp->tx_skbuff[i]); - yp->tx_skbuff[i] = NULL; - } - -#ifdef YF_PROTOTYPE /* Support for prototype hardware errata. */ - if (yellowfin_debug > 0) { - netdev_printk(KERN_DEBUG, dev, "Received %d frames that we should not have\n", - bogus_rx); - } -#endif - - return 0; -} - -/* Set or clear the multicast filter for this adaptor. */ - -static void set_rx_mode(struct net_device *dev) -{ - struct yellowfin_private *yp = netdev_priv(dev); - void __iomem *ioaddr = yp->base; - u16 cfg_value = ioread16(ioaddr + Cnfg); - - /* Stop the Rx process to change any value. */ - iowrite16(cfg_value & ~0x1000, ioaddr + Cnfg); - if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ - iowrite16(0x000F, ioaddr + AddrMode); - } else if ((netdev_mc_count(dev) > 64) || - (dev->flags & IFF_ALLMULTI)) { - /* Too many to filter well, or accept all multicasts. */ - iowrite16(0x000B, ioaddr + AddrMode); - } else if (!netdev_mc_empty(dev)) { /* Must use the multicast hash table. */ - struct netdev_hw_addr *ha; - u16 hash_table[4]; - int i; - - memset(hash_table, 0, sizeof(hash_table)); - netdev_for_each_mc_addr(ha, dev) { - unsigned int bit; - - /* Due to a bug in the early chip versions, multiple filter - slots must be set for each address. */ - if (yp->drv_flags & HasMulticastBug) { - bit = (ether_crc_le(3, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - bit = (ether_crc_le(4, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - bit = (ether_crc_le(5, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - } - bit = (ether_crc_le(6, ha->addr) >> 3) & 0x3f; - hash_table[bit >> 4] |= (1 << bit); - } - /* Copy the hash table to the chip. */ - for (i = 0; i < 4; i++) - iowrite16(hash_table[i], ioaddr + HashTbl + i*2); - iowrite16(0x0003, ioaddr + AddrMode); - } else { /* Normal, unicast/broadcast-only mode. */ - iowrite16(0x0001, ioaddr + AddrMode); - } - /* Restart the Rx process. */ - iowrite16(cfg_value | 0x1000, ioaddr + Cnfg); -} - -static void yellowfin_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - struct yellowfin_private *np = netdev_priv(dev); - - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); - strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); -} - -static const struct ethtool_ops ethtool_ops = { - .get_drvinfo = yellowfin_get_drvinfo -}; - -static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct yellowfin_private *np = netdev_priv(dev); - void __iomem *ioaddr = np->base; - struct mii_ioctl_data *data = if_mii(rq); - - switch(cmd) { - case SIOCGMIIPHY: /* Get address of MII PHY in use. */ - data->phy_id = np->phys[0] & 0x1f; - fallthrough; - - case SIOCGMIIREG: /* Read MII PHY register. */ - data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f, data->reg_num & 0x1f); - return 0; - - case SIOCSMIIREG: /* Write MII PHY register. */ - if (data->phy_id == np->phys[0]) { - u16 value = data->val_in; - switch (data->reg_num) { - case 0: - /* Check for autonegotiation on or reset. */ - np->medialock = (value & 0x9000) ? 0 : 1; - if (np->medialock) - np->full_duplex = (value & 0x0100) ? 1 : 0; - break; - case 4: np->advertising = value; break; - } - /* Perhaps check_duplex(dev), depending on chip semantics. */ - } - mdio_write(ioaddr, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); - return 0; - default: - return -EOPNOTSUPP; - } -} - - -static void yellowfin_remove_one(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct yellowfin_private *np; - - BUG_ON(!dev); - np = netdev_priv(dev); - - dma_free_coherent(&pdev->dev, STATUS_TOTAL_SIZE, np->tx_status, - np->tx_status_dma); - dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, - np->rx_ring_dma); - dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, - np->tx_ring_dma); - unregister_netdev (dev); - - pci_iounmap(pdev, np->base); - - pci_release_regions (pdev); - - free_netdev (dev); -} - - -static struct pci_driver yellowfin_driver = { - .name = DRV_NAME, - .id_table = yellowfin_pci_tbl, - .probe = yellowfin_init_one, - .remove = yellowfin_remove_one, -}; - - -static int __init yellowfin_init (void) -{ -/* when a module, this is printed whether or not devices are found in probe */ -#ifdef MODULE - printk(version); -#endif - return pci_register_driver(&yellowfin_driver); -} - - -static void __exit yellowfin_cleanup (void) -{ - pci_unregister_driver (&yellowfin_driver); -} - - -module_init(yellowfin_init); -module_exit(yellowfin_cleanup); diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 42c6dcfb1f0f..dd75c47758e1 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -5103,6 +5103,13 @@ static int qed_init_wfq_param(struct qed_hwfn *p_hwfn, return -EINVAL; } + /* All vports are already or become configured, nothing to distribute */ + if (non_requested_count == 0) { + p_hwfn->qm_info.wfq_data[vport_id].min_speed = req_rate; + p_hwfn->qm_info.wfq_data[vport_id].configured = true; + return 0; + } + total_left_rate = min_pf_rate - total_req_min_rate; left_rate_per_vp = total_left_rate / non_requested_count; diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 1dbfadb2a881..5f88733094d0 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -1108,9 +1108,12 @@ static int ravb_stop_dma(struct net_device *ndev) /* Request for transmission suspension */ ravb_modify(ndev, CCC, CCC_DTSR, CCC_DTSR); - error = ravb_wait(ndev, CSR, CSR_DTS, CSR_DTS); - if (error) - netdev_err(ndev, "failed to stop AXI BUS\n"); + /* Access to URAM will not be suspended if WoL is enabled. */ + if (!priv->wol_enabled) { + error = ravb_wait(ndev, CSR, CSR_DTS, CSR_DTS); + if (error) + netdev_err(ndev, "failed to stop AXI BUS\n"); + } /* Stop AVB-DMAC process */ return ravb_set_opmode(ndev, CCC_OPC_CONFIG); diff --git a/drivers/net/ethernet/renesas/rtsn.c b/drivers/net/ethernet/renesas/rtsn.c index 03a2669f0518..ee8381b60b8d 100644 --- a/drivers/net/ethernet/renesas/rtsn.c +++ b/drivers/net/ethernet/renesas/rtsn.c @@ -797,11 +797,11 @@ static int rtsn_mdio_alloc(struct rtsn_private *priv) /* Enter config mode before registering the MDIO bus */ ret = rtsn_reset(priv); if (ret) - goto out_free_bus; + goto out_put_node; ret = rtsn_change_mode(priv, OCR_OPC_CONFIG); if (ret) - goto out_free_bus; + goto out_put_node; rtsn_modify(priv, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, MPIC_PSMCS_DEFAULT | MPIC_PSMHT_DEFAULT); @@ -824,6 +824,8 @@ static int rtsn_mdio_alloc(struct rtsn_private *priv) return 0; +out_put_node: + of_node_put(mdio_node); out_free_bus: mdiobus_free(mii); return ret; diff --git a/drivers/net/ethernet/sfc/efx_devlink.c b/drivers/net/ethernet/sfc/efx_devlink.c index d842c60dfc10..e5c6f81af48b 100644 --- a/drivers/net/ethernet/sfc/efx_devlink.c +++ b/drivers/net/ethernet/sfc/efx_devlink.c @@ -531,7 +531,7 @@ static int efx_devlink_info_running_versions(struct efx_nic *efx, if (rc || outlength < MC_CMD_GET_VERSION_OUT_LEN) { netif_err(efx, drv, efx->net_dev, "mcdi MC_CMD_GET_VERSION failed\n"); - return rc; + return rc ?: -EIO; } /* Handle previous output */ diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index 13ce9086a9ca..66bca803b19c 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -19,21 +19,6 @@ config NET_VENDOR_SMSC if NET_VENDOR_SMSC -config SMC9194 - tristate "SMC 9194 support" - depends on ISA - select CRC32 - select NETDEV_LEGACY_INIT - help - This is support for the SMC9xxx based Ethernet cards. Choose this - option if you have a DELL laptop with the docking station, or - another SMC9192/9194 based chipset. Say Y if you want it compiled - into the kernel, and read the file - <file:Documentation/networking/device_drivers/ethernet/smsc/smc9.rst>. - - To compile this driver as a module, choose M here. The module - will be called smc9194. - config SMC91X tristate "SMC 91C9x/91C1xxx support" select CRC32 @@ -52,18 +37,6 @@ config SMC91X The module will be called smc91x. If you want to compile it as a module, say M here and read <file:Documentation/kbuild/modules.rst>. -config PCMCIA_SMC91C92 - tristate "SMC 91Cxx PCMCIA support" - depends on PCMCIA && HAS_IOPORT - select CRC32 - select MII - help - Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA - (PC-card) Ethernet or Fast Ethernet card to your computer. - - To compile this driver as a module, choose M here: the module will be - called smc91c92_cs. If unsure, say N. - config EPIC100 tristate "SMC EtherPower II" depends on PCI diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile index 1501fa364c13..ab6f03f7ba17 100644 --- a/drivers/net/ethernet/smsc/Makefile +++ b/drivers/net/ethernet/smsc/Makefile @@ -3,9 +3,7 @@ # Makefile for the SMSC network device drivers. # -obj-$(CONFIG_SMC9194) += smc9194.o obj-$(CONFIG_SMC91X) += smc91x.o -obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o obj-$(CONFIG_EPIC100) += epic100.o obj-$(CONFIG_SMSC9420) += smsc9420.o obj-$(CONFIG_SMSC911X) += smsc911x.o diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c deleted file mode 100644 index e2e7b1c68563..000000000000 --- a/drivers/net/ethernet/smsc/smc9194.c +++ /dev/null @@ -1,1535 +0,0 @@ -/*------------------------------------------------------------------------ - . smc9194.c - . This is a driver for SMC's 9000 series of Ethernet cards. - . - . Copyright (C) 1996 by Erik Stahlman - . This software may be used and distributed according to the terms - . of the GNU General Public License, incorporated herein by reference. - . - . "Features" of the SMC chip: - . 4608 byte packet memory. ( for the 91C92. Others have more ) - . EEPROM for configuration - . AUI/TP selection ( mine has 10Base2/10BaseT select ) - . - . Arguments: - . io = for the base address - . irq = for the IRQ - . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 ) - . - . author: - . Erik Stahlman ( erik@vt.edu ) - . contributors: - . Arnaldo Carvalho de Melo <acme@conectiva.com.br> - . - . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) - . - . Sources: - . o SMC databook - . o skeleton.c by Donald Becker ( becker@scyld.com ) - . o ( a LOT of advice from Becker as well ) - . - . History: - . 12/07/95 Erik Stahlman written, got receive/xmit handled - . 01/03/96 Erik Stahlman worked out some bugs, actually usable!!! :-) - . 01/06/96 Erik Stahlman cleaned up some, better testing, etc - . 01/29/96 Erik Stahlman fixed autoirq, added multicast - . 02/01/96 Erik Stahlman 1. disabled all interrupts in smc_reset - . 2. got rid of post-decrementing bug -- UGH. - . 02/13/96 Erik Stahlman Tried to fix autoirq failure. Added more - . descriptive error messages. - . 02/15/96 Erik Stahlman Fixed typo that caused detection failure - . 02/23/96 Erik Stahlman Modified it to fit into kernel tree - . Added support to change hardware address - . Cleared stats on opens - . 02/26/96 Erik Stahlman Trial support for Kernel 1.2.13 - . Kludge for automatic IRQ detection - . 03/04/96 Erik Stahlman Fixed kernel 1.3.70 + - . Fixed bug reported by Gardner Buchanan in - . smc_enable, with outw instead of outb - . 03/06/96 Erik Stahlman Added hardware multicast from Peter Cammaert - . 04/14/00 Heiko Pruessing (SMA Regelsysteme) Fixed bug in chip memory - . allocation - . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet - . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ" - . 11/08/01 Matt Domsch Use common crc32 function - ----------------------------------------------------------------------------*/ - -static const char version[] = - "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)"; - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/in.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/crc32.h> -#include <linux/errno.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/bitops.h> - -#include <asm/io.h> - -#include "smc9194.h" - -#define DRV_NAME "smc9194" - -/*------------------------------------------------------------------------ - . - . Configuration options, for the experienced user to change. - . - -------------------------------------------------------------------------*/ - -/* - . Do you want to use 32 bit xfers? This should work on all chips, as - . the chipset is designed to accommodate them. -*/ -#ifdef __sh__ -#undef USE_32_BIT -#else -#define USE_32_BIT 1 -#endif - -/* - .the SMC9194 can be at any of the following port addresses. To change, - .for a slightly different card, you can add it to the array. Keep in - .mind that the array must end in zero. -*/ - -struct devlist { - unsigned int port; - unsigned int irq; -}; - -static struct devlist smc_devlist[] __initdata = { - {.port = 0x200, .irq = 0}, - {.port = 0x220, .irq = 0}, - {.port = 0x240, .irq = 0}, - {.port = 0x260, .irq = 0}, - {.port = 0x280, .irq = 0}, - {.port = 0x2A0, .irq = 0}, - {.port = 0x2C0, .irq = 0}, - {.port = 0x2E0, .irq = 0}, - {.port = 0x300, .irq = 0}, - {.port = 0x320, .irq = 0}, - {.port = 0x340, .irq = 0}, - {.port = 0x360, .irq = 0}, - {.port = 0x380, .irq = 0}, - {.port = 0x3A0, .irq = 0}, - {.port = 0x3C0, .irq = 0}, - {.port = 0x3E0, .irq = 0}, - {.port = 0, .irq = 0}, -}; -/* - . Wait time for memory to be free. This probably shouldn't be - . tuned that much, as waiting for this means nothing else happens - . in the system -*/ -#define MEMORY_WAIT_TIME 16 - -/* - . DEBUGGING LEVELS - . - . 0 for normal operation - . 1 for slightly more details - . >2 for various levels of increasingly useless information - . 2 for interrupt tracking, status flags - . 3 for packet dumps, etc. -*/ -#define SMC_DEBUG 0 - -#if (SMC_DEBUG > 2 ) -#define PRINTK3(x) printk x -#else -#define PRINTK3(x) -#endif - -#if SMC_DEBUG > 1 -#define PRINTK2(x) printk x -#else -#define PRINTK2(x) -#endif - -#ifdef SMC_DEBUG -#define PRINTK(x) printk x -#else -#define PRINTK(x) -#endif - - -/*------------------------------------------------------------------------ - . - . The internal workings of the driver. If you are changing anything - . here with the SMC stuff, you should have the datasheet and known - . what you are doing. - . - -------------------------------------------------------------------------*/ -#define CARDNAME "SMC9194" - - -/* store this information for the driver.. */ -struct smc_local { - /* - If I have to wait until memory is available to send - a packet, I will store the skbuff here, until I get the - desired memory. Then, I'll send it out and free it. - */ - struct sk_buff * saved_skb; - - /* - . This keeps track of how many packets that I have - . sent out. When an TX_EMPTY interrupt comes, I know - . that all of these have been sent. - */ - int packets_waiting; -}; - - -/*----------------------------------------------------------------- - . - . The driver can be entered at any of the following entry points. - . - .------------------------------------------------------------------ */ - -/* - . This is called by register_netdev(). It is responsible for - . checking the portlist for the SMC9000 series chipset. If it finds - . one, then it will initialize the device, find the hardware information, - . and sets up the appropriate device parameters. - . NOTE: Interrupts are *OFF* when this procedure is called. - . - . NB:This shouldn't be static since it is referred to externally. -*/ -struct net_device *smc_init(int unit); - -/* - . The kernel calls this function when someone wants to use the device, - . typically 'ifconfig ethX up'. -*/ -static int smc_open(struct net_device *dev); - -/* - . Our watchdog timed out. Called by the networking layer -*/ -static void smc_timeout(struct net_device *dev, unsigned int txqueue); - -/* - . This is called by the kernel in response to 'ifconfig ethX down'. It - . is responsible for cleaning up everything that the open routine - . does, and maybe putting the card into a powerdown state. -*/ -static int smc_close(struct net_device *dev); - -/* - . Finally, a call to set promiscuous mode ( for TCPDUMP and related - . programs ) and multicast modes. -*/ -static void smc_set_multicast_list(struct net_device *dev); - - -/*--------------------------------------------------------------- - . - . Interrupt level calls.. - . - ----------------------------------------------------------------*/ - -/* - . Handles the actual interrupt -*/ -static irqreturn_t smc_interrupt(int irq, void *); -/* - . This is a separate procedure to handle the receipt of a packet, to - . leave the interrupt code looking slightly cleaner -*/ -static inline void smc_rcv( struct net_device *dev ); -/* - . This handles a TX interrupt, which is only called when an error - . relating to a packet is sent. -*/ -static inline void smc_tx( struct net_device * dev ); - -/* - ------------------------------------------------------------ - . - . Internal routines - . - ------------------------------------------------------------ -*/ - -/* - . Test if a given location contains a chip, trying to cause as - . little damage as possible if it's not a SMC chip. -*/ -static int smc_probe(struct net_device *dev, int ioaddr); - -/* - . A rather simple routine to print out a packet for debugging purposes. -*/ -#if SMC_DEBUG > 2 -static void print_packet( byte *, int ); -#endif - -#define tx_done(dev) 1 - -/* this is called to actually send the packet to the chip */ -static void smc_hardware_send_packet( struct net_device * dev ); - -/* Since I am not sure if I will have enough room in the chip's ram - . to store the packet, I call this routine, which either sends it - . now, or generates an interrupt when the card is ready for the - . packet */ -static netdev_tx_t smc_wait_to_send_packet( struct sk_buff * skb, - struct net_device *dev ); - -/* this does a soft reset on the device */ -static void smc_reset( int ioaddr ); - -/* Enable Interrupts, Receive, and Transmit */ -static void smc_enable( int ioaddr ); - -/* this puts the device in an inactive state */ -static void smc_shutdown( int ioaddr ); - -/* This routine will find the IRQ of the driver if one is not - . specified in the input to the device. */ -static int smc_findirq( int ioaddr ); - -/* - . Function: smc_reset( int ioaddr ) - . Purpose: - . This sets the SMC91xx chip to its normal state, hopefully from whatever - . mess that any other DOS driver has put it in. - . - . Maybe I should reset more registers to defaults in here? SOFTRESET should - . do that for me. - . - . Method: - . 1. send a SOFT RESET - . 2. wait for it to finish - . 3. enable autorelease mode - . 4. reset the memory management unit - . 5. clear all interrupts - . -*/ -static void smc_reset( int ioaddr ) -{ - /* This resets the registers mostly to defaults, but doesn't - affect EEPROM. That seems unnecessary */ - SMC_SELECT_BANK( 0 ); - outw( RCR_SOFTRESET, ioaddr + RCR ); - - /* this should pause enough for the chip to be happy */ - SMC_DELAY( ); - - /* Set the transmit and receive configuration registers to - default values */ - outw( RCR_CLEAR, ioaddr + RCR ); - outw( TCR_CLEAR, ioaddr + TCR ); - - /* set the control register to automatically - release successfully transmitted packets, to make the best - use out of our limited memory */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL ); - - /* Reset the MMU */ - SMC_SELECT_BANK( 2 ); - outw( MC_RESET, ioaddr + MMU_CMD ); - - /* Note: It doesn't seem that waiting for the MMU busy is needed here, - but this is a place where future chipsets _COULD_ break. Be wary - of issuing another MMU command right after this */ - - outb( 0, ioaddr + INT_MASK ); -} - -/* - . Function: smc_enable - . Purpose: let the chip talk to the outside work - . Method: - . 1. Enable the transmitter - . 2. Enable the receiver - . 3. Enable interrupts -*/ -static void smc_enable( int ioaddr ) -{ - SMC_SELECT_BANK( 0 ); - /* see the header file for options in TCR/RCR NORMAL*/ - outw( TCR_NORMAL, ioaddr + TCR ); - outw( RCR_NORMAL, ioaddr + RCR ); - - /* now, enable interrupts */ - SMC_SELECT_BANK( 2 ); - outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK ); -} - -/* - . Function: smc_shutdown - . Purpose: closes down the SMC91xxx chip. - . Method: - . 1. zero the interrupt mask - . 2. clear the enable receive flag - . 3. clear the enable xmit flags - . - . TODO: - . (1) maybe utilize power down mode. - . Why not yet? Because while the chip will go into power down mode, - . the manual says that it will wake up in response to any I/O requests - . in the register space. Empirical results do not show this working. -*/ -static void smc_shutdown( int ioaddr ) -{ - /* no more interrupts for me */ - SMC_SELECT_BANK( 2 ); - outb( 0, ioaddr + INT_MASK ); - - /* and tell the card to stay away from that nasty outside world */ - SMC_SELECT_BANK( 0 ); - outb( RCR_CLEAR, ioaddr + RCR ); - outb( TCR_CLEAR, ioaddr + TCR ); -#if 0 - /* finally, shut the chip down */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL ); -#endif -} - - -/* - . Function: smc_setmulticast( int ioaddr, struct net_device *dev ) - . Purpose: - . This sets the internal hardware table to filter out unwanted multicast - . packets before they take up memory. - . - . The SMC chip uses a hash table where the high 6 bits of the CRC of - . address are the offset into the table. If that bit is 1, then the - . multicast packet is accepted. Otherwise, it's dropped silently. - . - . To use the 6 bits as an offset into the table, the high 3 bits are the - . number of the 8 bit register, while the low 3 bits are the bit within - . that register. - . - . This routine is based very heavily on the one provided by Peter Cammaert. -*/ - - -static void smc_setmulticast(int ioaddr, struct net_device *dev) -{ - int i; - unsigned char multicast_table[ 8 ]; - struct netdev_hw_addr *ha; - /* table for flipping the order of 3 bits */ - unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - - /* start with a table of all zeros: reject all */ - memset( multicast_table, 0, sizeof( multicast_table ) ); - - netdev_for_each_mc_addr(ha, dev) { - int position; - - /* only use the low order bits */ - position = ether_crc_le(6, ha->addr) & 0x3f; - - /* do some messy swapping to put the bit in the right spot */ - multicast_table[invert3[position&7]] |= - (1<<invert3[(position>>3)&7]); - - } - /* now, the table can be loaded into the chipset */ - SMC_SELECT_BANK( 3 ); - - for ( i = 0; i < 8 ; i++ ) { - outb( multicast_table[i], ioaddr + MULTICAST1 + i ); - } -} - -/* - . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * ) - . Purpose: - . Attempt to allocate memory for a packet, if chip-memory is not - . available, then tell the card to generate an interrupt when it - . is available. - . - . Algorithm: - . - . o if the saved_skb is not currently null, then drop this packet - . on the floor. This should never happen, because of TBUSY. - . o if the saved_skb is null, then replace it with the current packet, - . o See if I can sending it now. - . o (NO): Enable interrupts and let the interrupt handler deal with it. - . o (YES):Send it now. -*/ -static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct smc_local *lp = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - word length; - unsigned short numPages; - word time_out; - - netif_stop_queue(dev); - /* Well, I want to send the packet.. but I don't know - if I can send it right now... */ - - if ( lp->saved_skb) { - /* THIS SHOULD NEVER HAPPEN. */ - dev->stats.tx_aborted_errors++; - printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); - return NETDEV_TX_BUSY; - } - lp->saved_skb = skb; - - length = skb->len; - - if (length < ETH_ZLEN) { - if (skb_padto(skb, ETH_ZLEN)) { - netif_wake_queue(dev); - return NETDEV_TX_OK; - } - length = ETH_ZLEN; - } - - /* - ** The MMU wants the number of pages to be the number of 256 bytes - ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) - ** - ** Pkt size for allocating is data length +6 (for additional status words, - ** length and ctl!) If odd size last byte is included in this header. - */ - numPages = ((length & 0xfffe) + 6) / 256; - - if (numPages > 7 ) { - printk(CARDNAME": Far too big packet error.\n"); - /* freeing the packet is a good thing here... but should - . any packets of this size get down here? */ - dev_kfree_skb (skb); - lp->saved_skb = NULL; - /* this IS an error, but, i don't want the skb saved */ - netif_wake_queue(dev); - return NETDEV_TX_OK; - } - /* either way, a packet is waiting now */ - lp->packets_waiting++; - - /* now, try to allocate the memory */ - SMC_SELECT_BANK( 2 ); - outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); - /* - . Performance Hack - . - . wait a short amount of time.. if I can send a packet now, I send - . it now. Otherwise, I enable an interrupt and wait for one to be - . available. - . - . I could have handled this a slightly different way, by checking to - . see if any memory was available in the FREE MEMORY register. However, - . either way, I need to generate an allocation, and the allocation works - . no matter what, so I saw no point in checking free memory. - */ - time_out = MEMORY_WAIT_TIME; - do { - word status; - - status = inb( ioaddr + INTERRUPT ); - if ( status & IM_ALLOC_INT ) { - /* acknowledge the interrupt */ - outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); - break; - } - } while ( -- time_out ); - - if ( !time_out ) { - /* oh well, wait until the chip finds memory later */ - SMC_ENABLE_INT( IM_ALLOC_INT ); - PRINTK2((CARDNAME": memory allocation deferred.\n")); - /* it's deferred, but I'll handle it later */ - return NETDEV_TX_OK; - } - /* or YES! I can send the packet now.. */ - smc_hardware_send_packet(dev); - netif_wake_queue(dev); - return NETDEV_TX_OK; -} - -/* - . Function: smc_hardware_send_packet(struct net_device * ) - . Purpose: - . This sends the actual packet to the SMC9xxx chip. - . - . Algorithm: - . First, see if a saved_skb is available. - . ( this should NOT be called if there is no 'saved_skb' - . Now, find the packet number that the chip allocated - . Point the data pointers at it in memory - . Set the length word in the chip's memory - . Dump the packet to chip memory - . Check if a last byte is needed ( odd length packet ) - . if so, set the control flag right - . Tell the card to send it - . Enable the transmit interrupt, so I know if it failed - . Free the kernel data if I actually sent it. -*/ -static void smc_hardware_send_packet( struct net_device * dev ) -{ - struct smc_local *lp = netdev_priv(dev); - byte packet_no; - struct sk_buff * skb = lp->saved_skb; - word length; - unsigned int ioaddr; - byte * buf; - - ioaddr = dev->base_addr; - - if ( !skb ) { - PRINTK((CARDNAME": In XMIT with no packet to send\n")); - return; - } - length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - buf = skb->data; - - /* If I get here, I _know_ there is a packet slot waiting for me */ - packet_no = inb( ioaddr + PNR_ARR + 1 ); - if ( packet_no & 0x80 ) { - /* or isn't there? BAD CHIP! */ - netdev_dbg(dev, CARDNAME": Memory allocation failed.\n"); - dev_kfree_skb_any(skb); - lp->saved_skb = NULL; - netif_wake_queue(dev); - return; - } - - /* we have a packet address, so tell the card to use it */ - outb( packet_no, ioaddr + PNR_ARR ); - - /* point to the beginning of the packet */ - outw( PTR_AUTOINC , ioaddr + POINTER ); - - PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length)); -#if SMC_DEBUG > 2 - print_packet( buf, length ); -#endif - - /* send the packet length ( +6 for status, length and ctl byte ) - and the status word ( set to zeros ) */ -#ifdef USE_32_BIT - outl( (length +6 ) << 16 , ioaddr + DATA_1 ); -#else - outw( 0, ioaddr + DATA_1 ); - /* send the packet length ( +6 for status words, length, and ctl*/ - outb( (length+6) & 0xFF,ioaddr + DATA_1 ); - outb( (length+6) >> 8 , ioaddr + DATA_1 ); -#endif - - /* send the actual data - . I _think_ it's faster to send the longs first, and then - . mop up by sending the last word. It depends heavily - . on alignment, at least on the 486. Maybe it would be - . a good idea to check which is optimal? But that could take - . almost as much time as is saved? - */ -#ifdef USE_32_BIT - if ( length & 0x2 ) { - outsl(ioaddr + DATA_1, buf, length >> 2 ); - outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); - } - else - outsl(ioaddr + DATA_1, buf, length >> 2 ); -#else - outsw(ioaddr + DATA_1 , buf, (length ) >> 1); -#endif - /* Send the last byte, if there is one. */ - - if ( (length & 1) == 0 ) { - outw( 0, ioaddr + DATA_1 ); - } else { - outb( buf[length -1 ], ioaddr + DATA_1 ); - outb( 0x20, ioaddr + DATA_1); - } - - /* enable the interrupts */ - SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); - - /* and let the chipset deal with it */ - outw( MC_ENQUEUE , ioaddr + MMU_CMD ); - - PRINTK2((CARDNAME": Sent packet of length %d\n", length)); - - lp->saved_skb = NULL; - dev_kfree_skb_any (skb); - - netif_trans_update(dev); - - /* we can send another packet */ - netif_wake_queue(dev); -} - -/*------------------------------------------------------------------------- - | - | smc_init(int unit) - | Input parameters: - | dev->base_addr == 0, try to find all possible locations - | dev->base_addr == 1, return failure code - | dev->base_addr == 2, always allocate space, and return success - | dev->base_addr == <anything else> this is the address to check - | - | Output: - | pointer to net_device or ERR_PTR(error) - | - --------------------------------------------------------------------------- -*/ -static int io; -static int irq; -static int ifport; - -struct net_device * __init smc_init(int unit) -{ - struct net_device *dev = alloc_etherdev(sizeof(struct smc_local)); - struct devlist *smcdev = smc_devlist; - int err = 0; - - if (!dev) - return ERR_PTR(-ENODEV); - - if (unit >= 0) { - sprintf(dev->name, "eth%d", unit); - netdev_boot_setup_check(dev); - io = dev->base_addr; - irq = dev->irq; - } - - if (io > 0x1ff) { /* Check a single specified location. */ - err = smc_probe(dev, io); - } else if (io != 0) { /* Don't probe at all. */ - err = -ENXIO; - } else { - for (;smcdev->port; smcdev++) { - if (smc_probe(dev, smcdev->port) == 0) - break; - } - if (!smcdev->port) - err = -ENODEV; - } - if (err) - goto out; - err = register_netdev(dev); - if (err) - goto out1; - return dev; -out1: - free_irq(dev->irq, dev); - release_region(dev->base_addr, SMC_IO_EXTENT); -out: - free_netdev(dev); - return ERR_PTR(err); -} - -/*---------------------------------------------------------------------- - . smc_findirq - . - . This routine has a simple purpose -- make the SMC chip generate an - . interrupt, so an auto-detect routine can detect it, and find the IRQ, - ------------------------------------------------------------------------ -*/ -static int __init smc_findirq(int ioaddr) -{ -#ifndef NO_AUTOPROBE - int timeout = 20; - unsigned long cookie; - - - cookie = probe_irq_on(); - - /* - * What I try to do here is trigger an ALLOC_INT. This is done - * by allocating a small chunk of memory, which will give an interrupt - * when done. - */ - - - SMC_SELECT_BANK(2); - /* enable ALLOCation interrupts ONLY */ - outb( IM_ALLOC_INT, ioaddr + INT_MASK ); - - /* - . Allocate 512 bytes of memory. Note that the chip was just - . reset so all the memory is available - */ - outw( MC_ALLOC | 1, ioaddr + MMU_CMD ); - - /* - . Wait until positive that the interrupt has been generated - */ - while ( timeout ) { - byte int_status; - - int_status = inb( ioaddr + INTERRUPT ); - - if ( int_status & IM_ALLOC_INT ) - break; /* got the interrupt */ - timeout--; - } - /* there is really nothing that I can do here if timeout fails, - as probe_irq_off will return a 0 anyway, which is what I - want in this case. Plus, the clean up is needed in both - cases. */ - - /* DELAY HERE! - On a fast machine, the status might change before the interrupt - is given to the processor. This means that the interrupt was - never detected, and probe_irq_off fails to report anything. - This should fix probe_irq_* problems. - */ - SMC_DELAY(); - SMC_DELAY(); - - /* and disable all interrupts again */ - outb( 0, ioaddr + INT_MASK ); - - /* and return what I found */ - return probe_irq_off(cookie); -#else /* NO_AUTOPROBE */ - struct devlist *smcdev; - for (smcdev = smc_devlist; smcdev->port; smcdev++) { - if (smcdev->port == ioaddr) - return smcdev->irq; - } - return 0; -#endif -} - -static const struct net_device_ops smc_netdev_ops = { - .ndo_open = smc_open, - .ndo_stop = smc_close, - .ndo_start_xmit = smc_wait_to_send_packet, - .ndo_tx_timeout = smc_timeout, - .ndo_set_rx_mode = smc_set_multicast_list, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -/*---------------------------------------------------------------------- - . Function: smc_probe( int ioaddr ) - . - . Purpose: - . Tests to see if a given ioaddr points to an SMC9xxx chip. - . Returns a 0 on success - . - . Algorithm: - . (1) see if the high byte of BANK_SELECT is 0x33 - . (2) compare the ioaddr with the base register's address - . (3) see if I recognize the chip ID in the appropriate register - . - .--------------------------------------------------------------------- - */ - -/*--------------------------------------------------------------- - . Here I do typical initialization tasks. - . - . o Initialize the structure if needed - . o print out my vanity message if not done so already - . o print out what type of hardware is detected - . o print out the ethernet address - . o find the IRQ - . o set up my private data - . o configure the dev structure with my subroutines - . o actually GRAB the irq. - . o GRAB the region - .----------------------------------------------------------------- -*/ -static int __init smc_probe(struct net_device *dev, int ioaddr) -{ - int i, memory, retval; - unsigned int bank; - - const char *version_string; - const char *if_string; - - /* registers */ - word revision_register; - word base_address_register; - word configuration_register; - word memory_info_register; - word memory_cfg_register; - u8 addr[ETH_ALEN]; - - /* Grab the region so that no one else tries to probe our ioports. */ - if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME)) - return -EBUSY; - - dev->irq = irq; - dev->if_port = ifport; - - /* First, see if the high byte is 0x33 */ - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* The above MIGHT indicate a device, but I need to write to further - test this. */ - outw( 0x0, ioaddr + BANK_SELECT ); - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00 ) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* well, we've already written once, so hopefully another time won't - hurt. This time, I need to switch the bank register to bank 1, - so I can access the base address register */ - SMC_SELECT_BANK(1); - base_address_register = inw( ioaddr + BASE ); - if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) { - printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). " - "Probably not a SMC chip\n", - ioaddr, base_address_register >> 3 & 0x3E0 ); - /* well, the base address register didn't match. Must not have - been a SMC chip after all. */ - retval = -ENODEV; - goto err_out; - } - - /* check if the revision register is something that I recognize. - These might need to be added to later, as future revisions - could be added. */ - SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) { - /* I don't recognize this chip, so... */ - printk(CARDNAME ": IO %x: Unrecognized revision register:" - " %x, Contact author.\n", ioaddr, revision_register); - - retval = -ENODEV; - goto err_out; - } - - /* at this point I'll assume that the chip is an SMC9xxx. - It might be prudent to check a listing of MAC addresses - against the hardware address, or do some other tests. */ - - pr_info_once("%s\n", version); - - /* fill in some of the fields */ - dev->base_addr = ioaddr; - - /* - . Get the MAC address ( bank 1, regs 4 - 9 ) - */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { - word address; - - address = inw( ioaddr + ADDR0 + i ); - addr[i + 1] = address >> 8; - addr[i] = address & 0xFF; - } - eth_hw_addr_set(dev, addr); - - /* get the memory information */ - - SMC_SELECT_BANK( 0 ); - memory_info_register = inw( ioaddr + MIR ); - memory_cfg_register = inw( ioaddr + MCR ); - memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */ - memory *= 256 * ( memory_info_register & 0xFF ); - - /* - Now, I want to find out more about the chip. This is sort of - redundant, but it's cleaner to have it in both, rather than having - one VERY long probe procedure. - */ - SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; - if ( !version_string ) { - /* I shouldn't get here because this call was done before.... */ - retval = -ENODEV; - goto err_out; - } - - /* is it using AUI or 10BaseT ? */ - if ( dev->if_port == 0 ) { - SMC_SELECT_BANK(1); - configuration_register = inw( ioaddr + CONFIG ); - if ( configuration_register & CFG_AUI_SELECT ) - dev->if_port = 2; - else - dev->if_port = 1; - } - if_string = interfaces[ dev->if_port - 1 ]; - - /* now, reset the chip, and put it into a known state */ - smc_reset( ioaddr ); - - /* - . If dev->irq is 0, then the device has to be banged on to see - . what the IRQ is. - . - . This banging doesn't always detect the IRQ, for unknown reasons. - . a workaround is to reset the chip and try again. - . - . Interestingly, the DOS packet driver *SETS* the IRQ on the card to - . be what is requested on the command line. I don't do that, mostly - . because the card that I have uses a non-standard method of accessing - . the IRQs, and because this _should_ work in most configurations. - . - . Specifying an IRQ is done with the assumption that the user knows - . what (s)he is doing. No checking is done!!!! - . - */ - if ( dev->irq < 2 ) { - int trials; - - trials = 3; - while ( trials-- ) { - dev->irq = smc_findirq( ioaddr ); - if ( dev->irq ) - break; - /* kick the card and try again */ - smc_reset( ioaddr ); - } - } - if (dev->irq == 0 ) { - printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); - retval = -ENODEV; - goto err_out; - } - - /* now, print out the card info, in a short format.. */ - - netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", - version_string, revision_register & 0xF, ioaddr, dev->irq, - if_string, memory); - /* - . Print the Ethernet address - */ - netdev_info(dev, "ADDR: %pM\n", dev->dev_addr); - - /* Grab the IRQ */ - retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev); - if (retval) { - netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n", - DRV_NAME, dev->irq, retval); - goto err_out; - } - - dev->netdev_ops = &smc_netdev_ops; - dev->watchdog_timeo = HZ/20; - - return 0; - -err_out: - release_region(ioaddr, SMC_IO_EXTENT); - return retval; -} - -#if SMC_DEBUG > 2 -static void print_packet( byte * buf, int length ) -{ -#if 0 - print_hex_dump_debug(DRV_NAME, DUMP_PREFIX_OFFSET, 16, 1, - buf, length, true); -#endif -} -#endif - - -/* - * Open and Initialize the board - * - * Set up everything, reset the card, etc .. - * - */ -static int smc_open(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - - int i; /* used to set hw ethernet address */ - - /* clear out all the junk that was put here before... */ - memset(netdev_priv(dev), 0, sizeof(struct smc_local)); - - /* reset the hardware */ - - smc_reset( ioaddr ); - smc_enable( ioaddr ); - - /* Select which interface to use */ - - SMC_SELECT_BANK( 1 ); - if ( dev->if_port == 1 ) { - outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT, - ioaddr + CONFIG ); - } - else if ( dev->if_port == 2 ) { - outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT, - ioaddr + CONFIG ); - } - - /* - According to Becker, I have to set the hardware address - at this point, because the (l)user can set it with an - ioctl. Easily done... - */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { - word address; - - address = dev->dev_addr[ i + 1 ] << 8 ; - address |= dev->dev_addr[ i ]; - outw( address, ioaddr + ADDR0 + i ); - } - - netif_start_queue(dev); - return 0; -} - -/*-------------------------------------------------------- - . Called by the kernel to send a packet out into the void - . of the net. This routine is largely based on - . skeleton.c, from Becker. - .-------------------------------------------------------- -*/ - -static void smc_timeout(struct net_device *dev, unsigned int txqueue) -{ - /* If we get here, some higher level has decided we are broken. - There should really be a "kick me" function call instead. */ - netdev_warn(dev, CARDNAME": transmit timed out, %s?\n", - tx_done(dev) ? "IRQ conflict" : "network cable problem"); - /* "kick" the adaptor */ - smc_reset( dev->base_addr ); - smc_enable( dev->base_addr ); - netif_trans_update(dev); /* prevent tx timeout */ - /* clear anything saved */ - ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL; - netif_wake_queue(dev); -} - -/*------------------------------------------------------------- - . - . smc_rcv - receive a packet from the card - . - . There is ( at least ) a packet waiting to be read from - . chip-memory. - . - . o Read the status - . o If an error, record it - . o otherwise, read in the packet - -------------------------------------------------------------- -*/ -static void smc_rcv(struct net_device *dev) -{ - int ioaddr = dev->base_addr; - int packet_number; - word status; - word packet_length; - - /* assume bank 2 */ - - packet_number = inw( ioaddr + FIFO_PORTS ); - - if ( packet_number & FP_RXEMPTY ) { - /* we got called , but nothing was on the FIFO */ - PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n")); - /* don't need to restore anything */ - return; - } - - /* start reading from the start of the packet */ - outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); - - /* First two words are status and packet_length */ - status = inw( ioaddr + DATA_1 ); - packet_length = inw( ioaddr + DATA_1 ); - - packet_length &= 0x07ff; /* mask off top bits */ - - PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length )); - /* - . the packet length contains 3 extra words : - . status, length, and an extra word with an odd byte . - */ - packet_length -= 6; - - if ( !(status & RS_ERRORS ) ){ - /* do stuff to make a new packet */ - struct sk_buff * skb; - byte * data; - - /* read one extra byte */ - if ( status & RS_ODDFRAME ) - packet_length++; - - /* set multicast stats */ - if ( status & RS_MULTICAST ) - dev->stats.multicast++; - - skb = netdev_alloc_skb(dev, packet_length + 5); - if ( skb == NULL ) { - dev->stats.rx_dropped++; - goto done; - } - - /* - ! This should work without alignment, but it could be - ! in the worse case - */ - - skb_reserve( skb, 2 ); /* 16 bit alignment */ - - data = skb_put( skb, packet_length); - -#ifdef USE_32_BIT - /* QUESTION: Like in the TX routine, do I want - to send the DWORDs or the bytes first, or some - mixture. A mixture might improve already slow PIO - performance */ - PRINTK3((" Reading %d dwords (and %d bytes)\n", - packet_length >> 2, packet_length & 3 )); - insl(ioaddr + DATA_1 , data, packet_length >> 2 ); - /* read the left over bytes */ - insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC), - packet_length & 0x3 ); -#else - PRINTK3((" Reading %d words and %d byte(s)\n", - (packet_length >> 1 ), packet_length & 1 )); - insw(ioaddr + DATA_1 , data, packet_length >> 1); - if ( packet_length & 1 ) { - data += packet_length & ~1; - *(data++) = inb( ioaddr + DATA_1 ); - } -#endif -#if SMC_DEBUG > 2 - print_packet( data, packet_length ); -#endif - - skb->protocol = eth_type_trans(skb, dev ); - netif_rx(skb); - dev->stats.rx_packets++; - dev->stats.rx_bytes += packet_length; - } else { - /* error ... */ - dev->stats.rx_errors++; - - if ( status & RS_ALGNERR ) dev->stats.rx_frame_errors++; - if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) - dev->stats.rx_length_errors++; - if ( status & RS_BADCRC) dev->stats.rx_crc_errors++; - } - -done: - /* error or good, tell the card to get rid of this packet */ - outw( MC_RELEASE, ioaddr + MMU_CMD ); -} - - -/************************************************************************* - . smc_tx - . - . Purpose: Handle a transmit error message. This will only be called - . when an error, because of the AUTO_RELEASE mode. - . - . Algorithm: - . Save pointer and packet no - . Get the packet no from the top of the queue - . check if it's valid ( if not, is this an error??? ) - . read the status word - . record the error - . ( resend? Not really, since we don't want old packets around ) - . Restore saved values - ************************************************************************/ -static void smc_tx( struct net_device * dev ) -{ - int ioaddr = dev->base_addr; - struct smc_local *lp = netdev_priv(dev); - byte saved_packet; - byte packet_no; - word tx_status; - - - /* assume bank 2 */ - - saved_packet = inb( ioaddr + PNR_ARR ); - packet_no = inw( ioaddr + FIFO_PORTS ); - packet_no &= 0x7F; - - /* select this as the packet to read from */ - outb( packet_no, ioaddr + PNR_ARR ); - - /* read the first word from this packet */ - outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER ); - - tx_status = inw( ioaddr + DATA_1 ); - PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status)); - - dev->stats.tx_errors++; - if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++; - if ( tx_status & TS_LATCOL ) { - netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n"); - dev->stats.tx_window_errors++; - } -#if 0 - if ( tx_status & TS_16COL ) { ... } -#endif - - if ( tx_status & TS_SUCCESS ) { - netdev_info(dev, CARDNAME": Successful packet caused interrupt\n"); - } - /* re-enable transmit */ - SMC_SELECT_BANK( 0 ); - outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR ); - - /* kill the packet */ - SMC_SELECT_BANK( 2 ); - outw( MC_FREEPKT, ioaddr + MMU_CMD ); - - /* one less packet waiting for me */ - lp->packets_waiting--; - - outb( saved_packet, ioaddr + PNR_ARR ); -} - -/*-------------------------------------------------------------------- - . - . This is the main routine of the driver, to handle the device when - . it needs some attention. - . - . So: - . first, save state of the chipset - . branch off into routines to handle each case, and acknowledge - . each to the interrupt register - . and finally restore state. - . - ---------------------------------------------------------------------*/ - -static irqreturn_t smc_interrupt(int irq, void * dev_id) -{ - struct net_device *dev = dev_id; - int ioaddr = dev->base_addr; - struct smc_local *lp = netdev_priv(dev); - - byte status; - word card_stats; - byte mask; - int timeout; - /* state registers */ - word saved_bank; - word saved_pointer; - int handled = 0; - - - PRINTK3((CARDNAME": SMC interrupt started\n")); - - saved_bank = inw( ioaddr + BANK_SELECT ); - - SMC_SELECT_BANK(2); - saved_pointer = inw( ioaddr + POINTER ); - - mask = inb( ioaddr + INT_MASK ); - /* clear all interrupts */ - outb( 0, ioaddr + INT_MASK ); - - - /* set a timeout value, so I don't stay here forever */ - timeout = 4; - - PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask)); - do { - /* read the status flag, and mask it */ - status = inb( ioaddr + INTERRUPT ) & mask; - if (!status ) - break; - - handled = 1; - - PRINTK3((KERN_WARNING CARDNAME - ": Handling interrupt status %x\n", status)); - - if (status & IM_RCV_INT) { - /* Got a packet(s). */ - PRINTK2((KERN_WARNING CARDNAME - ": Receive Interrupt\n")); - smc_rcv(dev); - } else if (status & IM_TX_INT ) { - PRINTK2((KERN_WARNING CARDNAME - ": TX ERROR handled\n")); - smc_tx(dev); - outb(IM_TX_INT, ioaddr + INTERRUPT ); - } else if (status & IM_TX_EMPTY_INT ) { - /* update stats */ - SMC_SELECT_BANK( 0 ); - card_stats = inw( ioaddr + COUNTER ); - /* single collisions */ - dev->stats.collisions += card_stats & 0xF; - card_stats >>= 4; - /* multiple collisions */ - dev->stats.collisions += card_stats & 0xF; - - /* these are for when linux supports these statistics */ - - SMC_SELECT_BANK( 2 ); - PRINTK2((KERN_WARNING CARDNAME - ": TX_BUFFER_EMPTY handled\n")); - outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); - mask &= ~IM_TX_EMPTY_INT; - dev->stats.tx_packets += lp->packets_waiting; - lp->packets_waiting = 0; - - } else if (status & IM_ALLOC_INT ) { - PRINTK2((KERN_DEBUG CARDNAME - ": Allocation interrupt\n")); - /* clear this interrupt so it doesn't happen again */ - mask &= ~IM_ALLOC_INT; - - smc_hardware_send_packet( dev ); - - /* enable xmit interrupts based on this */ - mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); - - /* and let the card send more packets to me */ - netif_wake_queue(dev); - - PRINTK2((CARDNAME": Handoff done successfully.\n")); - } else if (status & IM_RX_OVRN_INT ) { - dev->stats.rx_errors++; - dev->stats.rx_fifo_errors++; - outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); - } else if (status & IM_EPH_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n")); - } else if (status & IM_ERCV_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n")); - outb( IM_ERCV_INT, ioaddr + INTERRUPT ); - } - } while ( timeout -- ); - - - /* restore state register */ - SMC_SELECT_BANK( 2 ); - outb( mask, ioaddr + INT_MASK ); - - PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask)); - outw( saved_pointer, ioaddr + POINTER ); - - SMC_SELECT_BANK( saved_bank ); - - PRINTK3((CARDNAME ": Interrupt done\n")); - return IRQ_RETVAL(handled); -} - - -/*---------------------------------------------------- - . smc_close - . - . this makes the board clean up everything that it can - . and not talk to the outside world. Caused by - . an 'ifconfig ethX down' - . - -----------------------------------------------------*/ -static int smc_close(struct net_device *dev) -{ - netif_stop_queue(dev); - /* clear everything */ - smc_shutdown( dev->base_addr ); - - /* Update the statistics here. */ - return 0; -} - -/*----------------------------------------------------------- - . smc_set_multicast_list - . - . This routine will, depending on the values passed to it, - . either make it accept multicast packets, go into - . promiscuous mode ( for TCPDUMP and cousins ) or accept - . a select set of multicast packets -*/ -static void smc_set_multicast_list(struct net_device *dev) -{ - short ioaddr = dev->base_addr; - - SMC_SELECT_BANK(0); - if ( dev->flags & IFF_PROMISC ) - outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR ); - -/* BUG? I never disable promiscuous mode if multicasting was turned on. - Now, I turn off promiscuous mode, but I don't do anything to multicasting - when promiscuous mode is turned on. -*/ - - /* Here, I am setting this to accept all multicast packets. - I don't need to zero the multicast table, because the flag is - checked before the table is - */ - else if (dev->flags & IFF_ALLMULTI) - outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR ); - - /* We just get all multicast packets even if we only want them - . from one source. This will be changed at some future - . point. */ - else if (!netdev_mc_empty(dev)) { - /* support hardware multicasting */ - - /* be sure I get rid of flags I might have set */ - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); - /* NOTE: this has to set the bank, so make sure it is the - last thing called. The bank is set to zero at the top */ - smc_setmulticast(ioaddr, dev); - } - else { - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); - - /* - since I'm disabling all multicast entirely, I need to - clear the multicast list - */ - SMC_SELECT_BANK( 3 ); - outw( 0, ioaddr + MULTICAST1 ); - outw( 0, ioaddr + MULTICAST2 ); - outw( 0, ioaddr + MULTICAST3 ); - outw( 0, ioaddr + MULTICAST4 ); - } -} - -#ifdef MODULE - -static struct net_device *devSMC9194; -MODULE_DESCRIPTION("SMC 9194 Ethernet driver"); -MODULE_LICENSE("GPL"); - -module_param_hw(io, int, ioport, 0); -module_param_hw(irq, int, irq, 0); -module_param(ifport, int, 0); -MODULE_PARM_DESC(io, "SMC 99194 I/O base address"); -MODULE_PARM_DESC(irq, "SMC 99194 IRQ number"); -MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)"); - -static int __init smc_init_module(void) -{ - if (io == 0) - printk(KERN_WARNING - CARDNAME": You shouldn't use auto-probing with insmod!\n" ); - - /* copy the parameters from insmod into the device structure */ - devSMC9194 = smc_init(-1); - return PTR_ERR_OR_ZERO(devSMC9194); -} -module_init(smc_init_module); - -static void __exit smc_cleanup_module(void) -{ - unregister_netdev(devSMC9194); - free_irq(devSMC9194->irq, devSMC9194); - release_region(devSMC9194->base_addr, SMC_IO_EXTENT); - free_netdev(devSMC9194); -} -module_exit(smc_cleanup_module); - -#endif /* MODULE */ diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c deleted file mode 100644 index cc0c75694351..000000000000 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ /dev/null @@ -1,2059 +0,0 @@ -/*====================================================================== - - A PCMCIA ethernet driver for SMC91c92-based cards. - - This driver supports Megahertz PCMCIA ethernet cards; and - Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem - multifunction cards. - - Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net - - smc91c92_cs.c 1.122 2002/10/25 06:26:39 - - This driver contains code written by Donald Becker - (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au), - David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman - (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of - Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've - incorporated some parts of his driver here. I (Dave) wrote most - of the PCMCIA glue code, and the Ositech support code. Kelly - Stephens (kstephen@holli.com) added support for the Motorola - Mariner, with help from Allen Brost. - - This software may be used and distributed according to the terms of - the GNU General Public License, incorporated herein by reference. - -======================================================================*/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/crc32.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/ioport.h> -#include <linux/ethtool.h> -#include <linux/mii.h> -#include <linux/jiffies.h> -#include <linux/firmware.h> - -#include <pcmcia/cistpl.h> -#include <pcmcia/cisreg.h> -#include <pcmcia/ciscode.h> -#include <pcmcia/ds.h> -#include <pcmcia/ss.h> - -#include <asm/io.h> -#include <linux/uaccess.h> - -/*====================================================================*/ - -static const char *if_names[] = { "auto", "10baseT", "10base2"}; - -/* Firmware name */ -#define FIRMWARE_NAME "ositech/Xilinx7OD.bin" - -/* Module parameters */ - -MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(FIRMWARE_NAME); - -#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) - -/* - Transceiver/media type. - 0 = auto - 1 = 10baseT (and autoselect if #define AUTOSELECT), - 2 = AUI/10base2, -*/ -INT_MODULE_PARM(if_port, 0); - - -#define DRV_NAME "smc91c92_cs" -#define DRV_VERSION "1.123" - -/*====================================================================*/ - -/* Operational parameter that usually are not changed. */ - -/* Time in jiffies before concluding Tx hung */ -#define TX_TIMEOUT ((400*HZ)/1000) - -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -#define INTR_WORK 4 - -/* Times to check the check the chip before concluding that it doesn't - currently have room for another Tx packet. */ -#define MEMORY_WAIT_TIME 8 - -struct smc_private { - struct pcmcia_device *p_dev; - spinlock_t lock; - u_short manfid; - u_short cardid; - - struct sk_buff *saved_skb; - int packets_waiting; - void __iomem *base; - u_short cfg; - struct timer_list media; - int watchdog, tx_err; - u_short media_status; - u_short fast_poll; - u_short link_status; - struct mii_if_info mii_if; - int duplex; - int rx_ovrn; - unsigned long last_rx; -}; - -/* Special definitions for Megahertz multifunction cards */ -#define MEGAHERTZ_ISR 0x0380 - -/* Special function registers for Motorola Mariner */ -#define MOT_LAN 0x0000 -#define MOT_UART 0x0020 -#define MOT_EEPROM 0x20 - -#define MOT_NORMAL \ -(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA) - -/* Special function registers for Ositech cards */ -#define OSITECH_AUI_CTL 0x0c -#define OSITECH_PWRDOWN 0x0d -#define OSITECH_RESET 0x0e -#define OSITECH_ISR 0x0f -#define OSITECH_AUI_PWR 0x0c -#define OSITECH_RESET_ISR 0x0e - -#define OSI_AUI_PWR 0x40 -#define OSI_LAN_PWRDOWN 0x02 -#define OSI_MODEM_PWRDOWN 0x01 -#define OSI_LAN_RESET 0x02 -#define OSI_MODEM_RESET 0x01 - -/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */ -#define BANK_SELECT 14 /* Window select register. */ -#define SMC_SELECT_BANK(x) { outw(x, ioaddr + BANK_SELECT); } - -/* Bank 0 registers. */ -#define TCR 0 /* transmit control register */ -#define TCR_CLEAR 0 /* do NOTHING */ -#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ -#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */ -#define TCR_MONCSN 0x0400 /* Monitor Carrier. */ -#define TCR_FDUPLX 0x0800 /* Full duplex mode. */ -#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN - -#define EPH 2 /* Ethernet Protocol Handler report. */ -#define EPH_TX_SUC 0x0001 -#define EPH_SNGLCOL 0x0002 -#define EPH_MULCOL 0x0004 -#define EPH_LTX_MULT 0x0008 -#define EPH_16COL 0x0010 -#define EPH_SQET 0x0020 -#define EPH_LTX_BRD 0x0040 -#define EPH_TX_DEFR 0x0080 -#define EPH_LAT_COL 0x0200 -#define EPH_LOST_CAR 0x0400 -#define EPH_EXC_DEF 0x0800 -#define EPH_CTR_ROL 0x1000 -#define EPH_RX_OVRN 0x2000 -#define EPH_LINK_OK 0x4000 -#define EPH_TX_UNRN 0x8000 -#define MEMINFO 8 /* Memory Information Register */ -#define MEMCFG 10 /* Memory Configuration Register */ - -/* Bank 1 registers. */ -#define CONFIG 0 -#define CFG_MII_SELECT 0x8000 /* 91C100 only */ -#define CFG_NO_WAIT 0x1000 -#define CFG_FULL_STEP 0x0400 -#define CFG_SET_SQLCH 0x0200 -#define CFG_AUI_SELECT 0x0100 -#define CFG_16BIT 0x0080 -#define CFG_DIS_LINK 0x0040 -#define CFG_STATIC 0x0030 -#define CFG_IRQ_SEL_1 0x0004 -#define CFG_IRQ_SEL_0 0x0002 -#define BASE_ADDR 2 -#define ADDR0 4 -#define GENERAL 10 -#define CONTROL 12 -#define CTL_STORE 0x0001 -#define CTL_RELOAD 0x0002 -#define CTL_EE_SELECT 0x0004 -#define CTL_TE_ENABLE 0x0020 -#define CTL_CR_ENABLE 0x0040 -#define CTL_LE_ENABLE 0x0080 -#define CTL_AUTO_RELEASE 0x0800 -#define CTL_POWERDOWN 0x2000 - -/* Bank 2 registers. */ -#define MMU_CMD 0 -#define MC_ALLOC 0x20 /* or with number of 256 byte packets */ -#define MC_RESET 0x40 -#define MC_RELEASE 0x80 /* remove and release the current rx packet */ -#define MC_FREEPKT 0xA0 /* Release packet in PNR register */ -#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */ -#define PNR_ARR 2 -#define FIFO_PORTS 4 -#define FP_RXEMPTY 0x8000 -#define POINTER 6 -#define PTR_AUTO_INC 0x0040 -#define PTR_READ 0x2000 -#define PTR_AUTOINC 0x4000 -#define PTR_RCV 0x8000 -#define DATA_1 8 -#define INTERRUPT 12 -#define IM_RCV_INT 0x1 -#define IM_TX_INT 0x2 -#define IM_TX_EMPTY_INT 0x4 -#define IM_ALLOC_INT 0x8 -#define IM_RX_OVRN_INT 0x10 -#define IM_EPH_INT 0x20 - -#define RCR 4 -enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002, - RxEnable = 0x0100, RxStripCRC = 0x0200}; -#define RCR_SOFTRESET 0x8000 /* resets the chip */ -#define RCR_STRIP_CRC 0x200 /* strips CRC */ -#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */ -#define RCR_ALMUL 0x4 /* receive all multicast packets */ -#define RCR_PROMISC 0x2 /* enable promiscuous mode */ - -/* the normal settings for the RCR register : */ -#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE) -#define RCR_CLEAR 0x0 /* set it to a base state */ -#define COUNTER 6 - -/* BANK 3 -- not the same values as in smc9194! */ -#define MULTICAST0 0 -#define MULTICAST2 2 -#define MULTICAST4 4 -#define MULTICAST6 6 -#define MGMT 8 -#define REVISION 0x0a - -/* Transmit status bits. */ -#define TS_SUCCESS 0x0001 -#define TS_16COL 0x0010 -#define TS_LATCOL 0x0200 -#define TS_LOSTCAR 0x0400 - -/* Receive status bits. */ -#define RS_ALGNERR 0x8000 -#define RS_BADCRC 0x2000 -#define RS_ODDFRAME 0x1000 -#define RS_TOOLONG 0x0800 -#define RS_TOOSHORT 0x0400 -#define RS_MULTICAST 0x0001 -#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) - -#define set_bits(v, p) outw(inw(p)|(v), (p)) -#define mask_bits(v, p) outw(inw(p)&(v), (p)) - -/*====================================================================*/ - -static void smc91c92_detach(struct pcmcia_device *p_dev); -static int smc91c92_config(struct pcmcia_device *link); -static void smc91c92_release(struct pcmcia_device *link); - -static int smc_open(struct net_device *dev); -static int smc_close(struct net_device *dev); -static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue); -static netdev_tx_t smc_start_xmit(struct sk_buff *skb, - struct net_device *dev); -static irqreturn_t smc_interrupt(int irq, void *dev_id); -static void smc_rx(struct net_device *dev); -static void set_rx_mode(struct net_device *dev); -static int s9k_config(struct net_device *dev, struct ifmap *map); -static void smc_set_xcvr(struct net_device *dev, int if_port); -static void smc_reset(struct net_device *dev); -static void media_check(struct timer_list *t); -static void mdio_sync(unsigned int addr); -static int mdio_read(struct net_device *dev, int phy_id, int loc); -static void mdio_write(struct net_device *dev, int phy_id, int loc, int value); -static int smc_link_ok(struct net_device *dev); -static const struct ethtool_ops ethtool_ops; - -static const struct net_device_ops smc_netdev_ops = { - .ndo_open = smc_open, - .ndo_stop = smc_close, - .ndo_start_xmit = smc_start_xmit, - .ndo_tx_timeout = smc_tx_timeout, - .ndo_set_config = s9k_config, - .ndo_set_rx_mode = set_rx_mode, - .ndo_eth_ioctl = smc_ioctl, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static int smc91c92_probe(struct pcmcia_device *link) -{ - struct smc_private *smc; - struct net_device *dev; - - dev_dbg(&link->dev, "smc91c92_attach()\n"); - - /* Create new ethernet device */ - dev = alloc_etherdev(sizeof(struct smc_private)); - if (!dev) - return -ENOMEM; - smc = netdev_priv(dev); - smc->p_dev = link; - link->priv = dev; - - spin_lock_init(&smc->lock); - - /* The SMC91c92-specific entries in the device structure. */ - dev->netdev_ops = &smc_netdev_ops; - dev->ethtool_ops = ðtool_ops; - dev->watchdog_timeo = TX_TIMEOUT; - - smc->mii_if.dev = dev; - smc->mii_if.mdio_read = mdio_read; - smc->mii_if.mdio_write = mdio_write; - smc->mii_if.phy_id_mask = 0x1f; - smc->mii_if.reg_num_mask = 0x1f; - - return smc91c92_config(link); -} /* smc91c92_attach */ - -static void smc91c92_detach(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - dev_dbg(&link->dev, "smc91c92_detach\n"); - - unregister_netdev(dev); - - smc91c92_release(link); - - free_netdev(dev); -} /* smc91c92_detach */ - -/*====================================================================*/ - -static int cvt_ascii_address(struct net_device *dev, char *s) -{ - u8 mac[ETH_ALEN]; - int i, j, da, c; - - if (strlen(s) != 12) - return -1; - for (i = 0; i < 6; i++) { - da = 0; - for (j = 0; j < 2; j++) { - c = *s++; - da <<= 4; - da += ((c >= '0') && (c <= '9')) ? - (c - '0') : ((c & 0x0f) + 9); - } - mac[i] = da; - } - eth_hw_addr_set(dev, mac); - return 0; -} - -/*==================================================================== - - Configuration stuff for Megahertz cards - - mhz_3288_power() is used to power up a 3288's ethernet chip. - mhz_mfc_config() handles socket setup for multifunction (1144 - and 3288) cards. mhz_setup() gets a card's hardware ethernet - address. - -======================================================================*/ - -static int mhz_3288_power(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - u_char tmp; - - /* Read the ISR twice... */ - readb(smc->base+MEGAHERTZ_ISR); - udelay(5); - readb(smc->base+MEGAHERTZ_ISR); - - /* Pause 200ms... */ - mdelay(200); - - /* Now read and write the COR... */ - tmp = readb(smc->base + link->config_base + CISREG_COR); - udelay(5); - writeb(tmp, smc->base + link->config_base + CISREG_COR); - - return 0; -} - -static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - int k; - p_dev->io_lines = 16; - p_dev->resource[1]->start = p_dev->resource[0]->start; - p_dev->resource[1]->end = 8; - p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - p_dev->resource[0]->end = 16; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - for (k = 0; k < 0x400; k += 0x10) { - if (k & 0x80) - continue; - p_dev->resource[0]->start = k ^ 0x300; - if (!pcmcia_request_io(p_dev)) - return 0; - } - return -ENODEV; -} - -static int mhz_mfc_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - unsigned int offset; - int i; - - link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ | - CONF_AUTO_SET_IO; - - /* The Megahertz combo cards have modem-like CIS entries, so - we have to explicitly try a bunch of port combinations. */ - if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL)) - return -ENODEV; - - dev->base_addr = link->resource[0]->start; - - /* Allocate a memory window, for accessing the ISR */ - link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; - link->resource[2]->start = link->resource[2]->end = 0; - i = pcmcia_request_window(link, link->resource[2], 0); - if (i != 0) - return -ENODEV; - - smc->base = ioremap(link->resource[2]->start, - resource_size(link->resource[2])); - offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0; - i = pcmcia_map_mem_page(link, link->resource[2], offset); - if ((i == 0) && - (smc->manfid == MANFID_MEGAHERTZ) && - (smc->cardid == PRODID_MEGAHERTZ_EM3288)) - mhz_3288_power(link); - - return 0; -} - -static int pcmcia_get_versmac(struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv) -{ - struct net_device *dev = priv; - cisparse_t parse; - u8 *buf; - - if (pcmcia_parse_tuple(tuple, &parse)) - return -EINVAL; - - buf = parse.version_1.str + parse.version_1.ofs[3]; - - if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0)) - return 0; - - return -EINVAL; -}; - -static int mhz_setup(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - size_t len; - u8 *buf; - int rc; - - /* Read the station address from the CIS. It is stored as the last - (fourth) string in the Version 1 Version/ID tuple. */ - if ((link->prod_id[3]) && - (cvt_ascii_address(dev, link->prod_id[3]) == 0)) - return 0; - - /* Workarounds for broken cards start here. */ - /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */ - if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev)) - return 0; - - /* Another possibility: for the EM3288, in a special tuple */ - rc = -1; - len = pcmcia_get_tuple(link, 0x81, &buf); - if (buf && len >= 13) { - buf[12] = '\0'; - if (cvt_ascii_address(dev, buf) == 0) - rc = 0; - } - kfree(buf); - - return rc; -}; - -/*====================================================================== - - Configuration stuff for the Motorola Mariner - - mot_config() writes directly to the Mariner configuration - registers because the CIS is just bogus. - -======================================================================*/ - -static void mot_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - unsigned int iouart = link->resource[1]->start; - - /* Set UART base address and force map with COR bit 1 */ - writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0); - writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1); - writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR); - - /* Set SMC base address and force map with COR bit 1 */ - writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0); - writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1); - writeb(MOT_NORMAL, smc->base + MOT_LAN + CISREG_COR); - - /* Wait for things to settle down */ - mdelay(100); -} - -static int mot_setup(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - unsigned int ioaddr = dev->base_addr; - int i, wait, loop; - u8 mac[ETH_ALEN]; - u_int addr; - - /* Read Ethernet address from Serial EEPROM */ - - for (i = 0; i < 3; i++) { - SMC_SELECT_BANK(2); - outw(MOT_EEPROM + i, ioaddr + POINTER); - SMC_SELECT_BANK(1); - outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL); - - for (loop = wait = 0; loop < 200; loop++) { - udelay(10); - wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL)); - if (wait == 0) break; - } - - if (wait) - return -1; - - addr = inw(ioaddr + GENERAL); - mac[2*i] = addr & 0xff; - mac[2*i+1] = (addr >> 8) & 0xff; - } - eth_hw_addr_set(dev, mac); - - return 0; -} - -/*====================================================================*/ - -static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data) -{ - p_dev->resource[0]->end = 16; - p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; - p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; - - return pcmcia_request_io(p_dev); -} - -static int smc_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - int i; - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO; - - i = pcmcia_loop_config(link, smc_configcheck, NULL); - if (!i) - dev->base_addr = link->resource[0]->start; - - return i; -} - - -static int smc_setup(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - /* Check for a LAN function extension tuple */ - if (!pcmcia_get_mac_from_cis(link, dev)) - return 0; - - /* Try the third string in the Version 1 Version/ID tuple. */ - if (link->prod_id[2]) { - if (cvt_ascii_address(dev, link->prod_id[2]) == 0) - return 0; - } - return -1; -} - -/*====================================================================*/ - -static int osi_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; - int i, j; - - link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ; - link->resource[0]->end = 64; - link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; - link->resource[1]->end = 8; - - /* Enable Hard Decode, LAN, Modem */ - link->io_lines = 16; - link->config_index = 0x23; - - for (i = j = 0; j < 4; j++) { - link->resource[1]->start = com[j]; - i = pcmcia_request_io(link); - if (i == 0) - break; - } - if (i != 0) { - /* Fallback: turn off hard decode */ - link->config_index = 0x03; - link->resource[1]->end = 0; - i = pcmcia_request_io(link); - } - dev->base_addr = link->resource[0]->start + 0x10; - return i; -} - -static int osi_load_firmware(struct pcmcia_device *link) -{ - const struct firmware *fw; - int i, err; - - err = request_firmware(&fw, FIRMWARE_NAME, &link->dev); - if (err) { - pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME); - return err; - } - - /* Download the Seven of Diamonds firmware */ - for (i = 0; i < fw->size; i++) { - outb(fw->data[i], link->resource[0]->start + 2); - udelay(50); - } - release_firmware(fw); - return err; -} - -static int pcmcia_osi_mac(struct pcmcia_device *p_dev, - tuple_t *tuple, - void *priv) -{ - struct net_device *dev = priv; - - if (tuple->TupleDataLen < 8) - return -EINVAL; - if (tuple->TupleData[0] != 0x04) - return -EINVAL; - - eth_hw_addr_set(dev, &tuple->TupleData[2]); - return 0; -}; - - -static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid) -{ - struct net_device *dev = link->priv; - int rc; - - /* Read the station address from tuple 0x90, subtuple 0x04 */ - if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev)) - return -1; - - if (((manfid == MANFID_OSITECH) && - (cardid == PRODID_OSITECH_SEVEN)) || - ((manfid == MANFID_PSION) && - (cardid == PRODID_PSION_NET100))) { - rc = osi_load_firmware(link); - if (rc) - return rc; - } else if (manfid == MANFID_OSITECH) { - /* Make sure both functions are powered up */ - set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR); - /* Now, turn on the interrupt for both card functions */ - set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR); - dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n", - inw(link->resource[0]->start + OSITECH_AUI_PWR), - inw(link->resource[0]->start + OSITECH_RESET_ISR)); - } - return 0; -} - -static int smc91c92_suspend(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - - if (link->open) - netif_device_detach(dev); - - return 0; -} - -static int smc91c92_resume(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - int i; - - if ((smc->manfid == MANFID_MEGAHERTZ) && - (smc->cardid == PRODID_MEGAHERTZ_EM3288)) - mhz_3288_power(link); - if (smc->manfid == MANFID_MOTOROLA) - mot_config(link); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) { - /* Power up the card and enable interrupts */ - set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); - set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); - } - if (((smc->manfid == MANFID_OSITECH) && - (smc->cardid == PRODID_OSITECH_SEVEN)) || - ((smc->manfid == MANFID_PSION) && - (smc->cardid == PRODID_PSION_NET100))) { - i = osi_load_firmware(link); - if (i) { - netdev_err(dev, "Failed to load firmware\n"); - return i; - } - } - if (link->open) { - smc_reset(dev); - netif_device_attach(dev); - } - - return 0; -} - - -/*====================================================================== - - This verifies that the chip is some SMC91cXX variant, and returns - the revision code if successful. Otherwise, it returns -ENODEV. - -======================================================================*/ - -static int check_sig(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - unsigned int ioaddr = dev->base_addr; - int width; - u_short s; - - SMC_SELECT_BANK(1); - if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) { - /* Try powering up the chip */ - outw(0, ioaddr + CONTROL); - mdelay(55); - } - - /* Try setting bus width */ - width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO); - s = inb(ioaddr + CONFIG); - if (width) - s |= CFG_16BIT; - else - s &= ~CFG_16BIT; - outb(s, ioaddr + CONFIG); - - /* Check Base Address Register to make sure bus width is OK */ - s = inw(ioaddr + BASE_ADDR); - if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) && - ((s >> 8) != (s & 0xff))) { - SMC_SELECT_BANK(3); - s = inw(ioaddr + REVISION); - return s & 0xff; - } - - if (width) { - netdev_info(dev, "using 8-bit IO window\n"); - - smc91c92_suspend(link); - pcmcia_fixup_iowidth(link); - smc91c92_resume(link); - return check_sig(link); - } - return -ENODEV; -} - -static int smc91c92_config(struct pcmcia_device *link) -{ - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - char *name; - int i, rev, j = 0; - unsigned int ioaddr; - u_long mir; - - dev_dbg(&link->dev, "smc91c92_config\n"); - - smc->manfid = link->manf_id; - smc->cardid = link->card_id; - - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) { - i = osi_config(link); - } else if ((smc->manfid == MANFID_MOTOROLA) || - ((smc->manfid == MANFID_MEGAHERTZ) && - ((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) || - (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) { - i = mhz_mfc_config(link); - } else { - i = smc_config(link); - } - if (i) - goto config_failed; - - i = pcmcia_request_irq(link, smc_interrupt); - if (i) - goto config_failed; - i = pcmcia_enable_device(link); - if (i) - goto config_failed; - - if (smc->manfid == MANFID_MOTOROLA) - mot_config(link); - - dev->irq = link->irq; - - if ((if_port >= 0) && (if_port <= 2)) - dev->if_port = if_port; - else - dev_notice(&link->dev, "invalid if_port requested\n"); - - switch (smc->manfid) { - case MANFID_OSITECH: - case MANFID_PSION: - i = osi_setup(link, smc->manfid, smc->cardid); break; - case MANFID_SMC: - case MANFID_NEW_MEDIA: - i = smc_setup(link); break; - case 0x128: /* For broken Megahertz cards */ - case MANFID_MEGAHERTZ: - i = mhz_setup(link); break; - case MANFID_MOTOROLA: - default: /* get the hw address from EEPROM */ - i = mot_setup(link); break; - } - - if (i != 0) { - dev_notice(&link->dev, "Unable to find hardware address.\n"); - goto config_failed; - } - - smc->duplex = 0; - smc->rx_ovrn = 0; - - rev = check_sig(link); - name = "???"; - if (rev > 0) - switch (rev >> 4) { - case 3: name = "92"; break; - case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break; - case 5: name = "95"; break; - case 7: name = "100"; break; - case 8: name = "100-FD"; break; - case 9: name = "110"; break; - } - - ioaddr = dev->base_addr; - if (rev > 0) { - u_long mcr; - SMC_SELECT_BANK(0); - mir = inw(ioaddr + MEMINFO) & 0xff; - if (mir == 0xff) mir++; - /* Get scale factor for memory size */ - mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200; - mir *= 128 * (1<<((mcr >> 9) & 7)); - SMC_SELECT_BANK(1); - smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT; - smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC; - if (smc->manfid == MANFID_OSITECH) - smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0; - if ((rev >> 4) >= 7) - smc->cfg |= CFG_MII_SELECT; - } else - mir = 0; - - if (smc->cfg & CFG_MII_SELECT) { - SMC_SELECT_BANK(3); - - for (i = 0; i < 32; i++) { - j = mdio_read(dev, i, 1); - if ((j != 0) && (j != 0xffff)) break; - } - smc->mii_if.phy_id = (i < 32) ? i : -1; - - SMC_SELECT_BANK(0); - } - - SET_NETDEV_DEV(dev, &link->dev); - - if (register_netdev(dev) != 0) { - dev_err(&link->dev, "register_netdev() failed\n"); - goto config_undo; - } - - netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n", - name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr); - - if (rev > 0) { - if (mir & 0x3ff) - netdev_info(dev, " %lu byte", mir); - else - netdev_info(dev, " %lu kb", mir>>10); - pr_cont(" buffer, %s xcvr\n", - (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]); - } - - if (smc->cfg & CFG_MII_SELECT) { - if (smc->mii_if.phy_id != -1) { - netdev_dbg(dev, " MII transceiver at index %d, status %x\n", - smc->mii_if.phy_id, j); - } else { - netdev_notice(dev, " No MII transceivers found!\n"); - } - } - return 0; - -config_undo: - unregister_netdev(dev); -config_failed: - smc91c92_release(link); - free_netdev(dev); - return -ENODEV; -} /* smc91c92_config */ - -static void smc91c92_release(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "smc91c92_release\n"); - if (link->resource[2]->end) { - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - iounmap(smc->base); - } - pcmcia_disable_device(link); -} - -/*====================================================================== - - MII interface support for SMC91cXX based cards -======================================================================*/ - -#define MDIO_SHIFT_CLK 0x04 -#define MDIO_DATA_OUT 0x01 -#define MDIO_DIR_WRITE 0x08 -#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE) -#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT) -#define MDIO_DATA_READ 0x02 - -static void mdio_sync(unsigned int addr) -{ - int bits; - for (bits = 0; bits < 32; bits++) { - outb(MDIO_DATA_WRITE1, addr); - outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); - } -} - -static int mdio_read(struct net_device *dev, int phy_id, int loc) -{ - unsigned int addr = dev->base_addr + MGMT; - u_int cmd = (0x06<<10)|(phy_id<<5)|loc; - int i, retval = 0; - - mdio_sync(addr); - for (i = 13; i >= 0; i--) { - int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; - outb(dat, addr); - outb(dat | MDIO_SHIFT_CLK, addr); - } - for (i = 19; i > 0; i--) { - outb(0, addr); - retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0); - outb(MDIO_SHIFT_CLK, addr); - } - return (retval>>1) & 0xffff; -} - -static void mdio_write(struct net_device *dev, int phy_id, int loc, int value) -{ - unsigned int addr = dev->base_addr + MGMT; - u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; - int i; - - mdio_sync(addr); - for (i = 31; i >= 0; i--) { - int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; - outb(dat, addr); - outb(dat | MDIO_SHIFT_CLK, addr); - } - for (i = 1; i >= 0; i--) { - outb(0, addr); - outb(MDIO_SHIFT_CLK, addr); - } -} - -/*====================================================================== - - The driver core code, most of which should be common with a - non-PCMCIA implementation. - -======================================================================*/ - -#ifdef PCMCIA_DEBUG -static void smc_dump(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - u_short i, w, save; - save = inw(ioaddr + BANK_SELECT); - for (w = 0; w < 4; w++) { - SMC_SELECT_BANK(w); - netdev_dbg(dev, "bank %d: ", w); - for (i = 0; i < 14; i += 2) - pr_cont(" %04x", inw(ioaddr + i)); - pr_cont("\n"); - } - outw(save, ioaddr + BANK_SELECT); -} -#endif - -static int smc_open(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - struct pcmcia_device *link = smc->p_dev; - - dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n", - dev->name, dev, inw(dev->base_addr + BANK_SELECT)); -#ifdef PCMCIA_DEBUG - smc_dump(dev); -#endif - - /* Check that the PCMCIA card is still here. */ - if (!pcmcia_dev_present(link)) - return -ENODEV; - /* Physical device present signature. */ - if (check_sig(link) < 0) { - netdev_info(dev, "Yikes! Bad chip signature!\n"); - return -ENODEV; - } - link->open++; - - netif_start_queue(dev); - smc->saved_skb = NULL; - smc->packets_waiting = 0; - - smc_reset(dev); - timer_setup(&smc->media, media_check, 0); - mod_timer(&smc->media, jiffies + HZ); - - return 0; -} /* smc_open */ - -/*====================================================================*/ - -static int smc_close(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - struct pcmcia_device *link = smc->p_dev; - unsigned int ioaddr = dev->base_addr; - - dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n", - dev->name, inw(ioaddr + BANK_SELECT)); - - netif_stop_queue(dev); - - /* Shut off all interrupts, and turn off the Tx and Rx sections. - Don't bother to check for chip present. */ - SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */ - outw(0, ioaddr + INTERRUPT); - SMC_SELECT_BANK(0); - mask_bits(0xff00, ioaddr + RCR); - mask_bits(0xff00, ioaddr + TCR); - - /* Put the chip into power-down mode. */ - SMC_SELECT_BANK(1); - outw(CTL_POWERDOWN, ioaddr + CONTROL ); - - link->open--; - timer_delete_sync(&smc->media); - - return 0; -} /* smc_close */ - -/*====================================================================== - - Transfer a packet to the hardware and trigger the packet send. - This may be called at either from either the Tx queue code - or the interrupt handler. - -======================================================================*/ - -static void smc_hardware_send_packet(struct net_device * dev) -{ - struct smc_private *smc = netdev_priv(dev); - struct sk_buff *skb = smc->saved_skb; - unsigned int ioaddr = dev->base_addr; - u_char packet_no; - - if (!skb) { - netdev_err(dev, "In XMIT with no packet to send\n"); - return; - } - - /* There should be a packet slot waiting. */ - packet_no = inw(ioaddr + PNR_ARR) >> 8; - if (packet_no & 0x80) { - /* If not, there is a hardware problem! Likely an ejected card. */ - netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n", - packet_no); - dev_kfree_skb_irq(skb); - smc->saved_skb = NULL; - netif_start_queue(dev); - return; - } - - dev->stats.tx_bytes += skb->len; - /* The card should use the just-allocated buffer. */ - outw(packet_no, ioaddr + PNR_ARR); - /* point to the beginning of the packet */ - outw(PTR_AUTOINC , ioaddr + POINTER); - - /* Send the packet length (+6 for status, length and ctl byte) - and the status word (set to zeros). */ - { - u_char *buf = skb->data; - u_int length = skb->len; /* The chip will pad to ethernet min. */ - - netdev_dbg(dev, "Trying to xmit packet of length %d\n", length); - - /* send the packet length: +6 for status word, length, and ctl */ - outw(0, ioaddr + DATA_1); - outw(length + 6, ioaddr + DATA_1); - outsw(ioaddr + DATA_1, buf, length >> 1); - - /* The odd last byte, if there is one, goes in the control word. */ - outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1); - } - - /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */ - outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) | - (inw(ioaddr + INTERRUPT) & 0xff00), - ioaddr + INTERRUPT); - - /* The chip does the rest of the work. */ - outw(MC_ENQUEUE , ioaddr + MMU_CMD); - - smc->saved_skb = NULL; - dev_kfree_skb_irq(skb); - netif_trans_update(dev); - netif_start_queue(dev); -} - -/*====================================================================*/ - -static void smc_tx_timeout(struct net_device *dev, unsigned int txqueue) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - - netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n", - inw(ioaddr)&0xff, inw(ioaddr + 2)); - dev->stats.tx_errors++; - smc_reset(dev); - netif_trans_update(dev); /* prevent tx timeout */ - smc->saved_skb = NULL; - netif_wake_queue(dev); -} - -static netdev_tx_t smc_start_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u_short num_pages; - short time_out, ir; - unsigned long flags; - - netif_stop_queue(dev); - - netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n", - skb->len, inw(ioaddr + 2)); - - if (smc->saved_skb) { - /* THIS SHOULD NEVER HAPPEN. */ - dev->stats.tx_aborted_errors++; - netdev_dbg(dev, "Internal error -- sent packet while busy\n"); - return NETDEV_TX_BUSY; - } - smc->saved_skb = skb; - - num_pages = skb->len >> 8; - - if (num_pages > 7) { - netdev_err(dev, "Far too big packet error: %d pages\n", num_pages); - dev_kfree_skb (skb); - smc->saved_skb = NULL; - dev->stats.tx_dropped++; - return NETDEV_TX_OK; /* Do not re-queue this packet. */ - } - /* A packet is now waiting. */ - smc->packets_waiting++; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */ - - /* need MC_RESET to keep the memory consistent. errata? */ - if (smc->rx_ovrn) { - outw(MC_RESET, ioaddr + MMU_CMD); - smc->rx_ovrn = 0; - } - - /* Allocate the memory; send the packet now if we win. */ - outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD); - for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) { - ir = inw(ioaddr+INTERRUPT); - if (ir & IM_ALLOC_INT) { - /* Acknowledge the interrupt, send the packet. */ - outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT); - smc_hardware_send_packet(dev); /* Send the packet now.. */ - spin_unlock_irqrestore(&smc->lock, flags); - return NETDEV_TX_OK; - } - } - - /* Otherwise defer until the Tx-space-allocated interrupt. */ - netdev_dbg(dev, "memory allocation deferred.\n"); - outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); - spin_unlock_irqrestore(&smc->lock, flags); - - return NETDEV_TX_OK; -} - -/*====================================================================== - - Handle a Tx anomalous event. Entered while in Window 2. - -======================================================================*/ - -static void smc_tx_err(struct net_device * dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - int saved_packet = inw(ioaddr + PNR_ARR) & 0xff; - int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f; - int tx_status; - - /* select this as the packet to read from */ - outw(packet_no, ioaddr + PNR_ARR); - - /* read the first word from this packet */ - outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER); - - tx_status = inw(ioaddr + DATA_1); - - dev->stats.tx_errors++; - if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++; - if (tx_status & TS_LATCOL) dev->stats.tx_window_errors++; - if (tx_status & TS_16COL) { - dev->stats.tx_aborted_errors++; - smc->tx_err++; - } - - if (tx_status & TS_SUCCESS) { - netdev_notice(dev, "Successful packet caused error interrupt?\n"); - } - /* re-enable transmit */ - SMC_SELECT_BANK(0); - outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); - SMC_SELECT_BANK(2); - - outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */ - - /* one less packet waiting for me */ - smc->packets_waiting--; - - outw(saved_packet, ioaddr + PNR_ARR); -} - -/*====================================================================*/ - -static void smc_eph_irq(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u_short card_stats, ephs; - - SMC_SELECT_BANK(0); - ephs = inw(ioaddr + EPH); - netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n", - ephs); - /* Could be a counter roll-over warning: update stats. */ - card_stats = inw(ioaddr + COUNTER); - /* single collisions */ - dev->stats.collisions += card_stats & 0xF; - card_stats >>= 4; - /* multiple collisions */ - dev->stats.collisions += card_stats & 0xF; -#if 0 /* These are for when linux supports these statistics */ - card_stats >>= 4; /* deferred */ - card_stats >>= 4; /* excess deferred */ -#endif - /* If we had a transmit error we must re-enable the transmitter. */ - outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); - - /* Clear a link error interrupt. */ - SMC_SELECT_BANK(1); - outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL); - outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, - ioaddr + CONTROL); - SMC_SELECT_BANK(2); -} - -/*====================================================================*/ - -static irqreturn_t smc_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = dev_id; - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr; - u_short saved_bank, saved_pointer, mask, status; - unsigned int handled = 1; - char bogus_cnt = INTR_WORK; /* Work we are willing to do. */ - - if (!netif_device_present(dev)) - return IRQ_NONE; - - ioaddr = dev->base_addr; - - netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n", - irq, ioaddr); - - spin_lock(&smc->lock); - smc->watchdog = 0; - saved_bank = inw(ioaddr + BANK_SELECT); - if ((saved_bank & 0xff00) != 0x3300) { - /* The device does not exist -- the card could be off-line, or - maybe it has been ejected. */ - netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n", - irq); - handled = 0; - goto irq_done; - } - - SMC_SELECT_BANK(2); - saved_pointer = inw(ioaddr + POINTER); - mask = inw(ioaddr + INTERRUPT) >> 8; - /* clear all interrupts */ - outw(0, ioaddr + INTERRUPT); - - do { /* read the status flag, and mask it */ - status = inw(ioaddr + INTERRUPT) & 0xff; - netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n", - status, mask); - if ((status & mask) == 0) { - if (bogus_cnt == INTR_WORK) - handled = 0; - break; - } - if (status & IM_RCV_INT) { - /* Got a packet(s). */ - smc_rx(dev); - } - if (status & IM_TX_INT) { - smc_tx_err(dev); - outw(IM_TX_INT, ioaddr + INTERRUPT); - } - status &= mask; - if (status & IM_TX_EMPTY_INT) { - outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT); - mask &= ~IM_TX_EMPTY_INT; - dev->stats.tx_packets += smc->packets_waiting; - smc->packets_waiting = 0; - } - if (status & IM_ALLOC_INT) { - /* Clear this interrupt so it doesn't happen again */ - mask &= ~IM_ALLOC_INT; - - smc_hardware_send_packet(dev); - - /* enable xmit interrupts based on this */ - mask |= (IM_TX_EMPTY_INT | IM_TX_INT); - - /* and let the card send more packets to me */ - netif_wake_queue(dev); - } - if (status & IM_RX_OVRN_INT) { - dev->stats.rx_errors++; - dev->stats.rx_fifo_errors++; - if (smc->duplex) - smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */ - outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT); - } - if (status & IM_EPH_INT) - smc_eph_irq(dev); - } while (--bogus_cnt); - - netdev_dbg(dev, " Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n", - mask, saved_bank, saved_pointer); - - /* restore state register */ - outw((mask<<8), ioaddr + INTERRUPT); - outw(saved_pointer, ioaddr + POINTER); - SMC_SELECT_BANK(saved_bank); - - netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq); - -irq_done: - - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) { - /* Retrigger interrupt if needed */ - mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR); - set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR); - } - if (smc->manfid == MANFID_MOTOROLA) { - u_char cor; - cor = readb(smc->base + MOT_UART + CISREG_COR); - writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR); - writeb(cor, smc->base + MOT_UART + CISREG_COR); - cor = readb(smc->base + MOT_LAN + CISREG_COR); - writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR); - writeb(cor, smc->base + MOT_LAN + CISREG_COR); - } - - if ((smc->base != NULL) && /* Megahertz MFC's */ - (smc->manfid == MANFID_MEGAHERTZ) && - (smc->cardid == PRODID_MEGAHERTZ_EM3288)) { - - u_char tmp; - tmp = readb(smc->base+MEGAHERTZ_ISR); - tmp = readb(smc->base+MEGAHERTZ_ISR); - - /* Retrigger interrupt if needed */ - writeb(tmp, smc->base + MEGAHERTZ_ISR); - writeb(tmp, smc->base + MEGAHERTZ_ISR); - } - - spin_unlock(&smc->lock); - return IRQ_RETVAL(handled); -} - -/*====================================================================*/ - -static void smc_rx(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - int rx_status; - int packet_length; /* Caution: not frame length, rather words - to transfer from the chip. */ - - /* Assertion: we are in Window 2. */ - - if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) { - netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n"); - return; - } - - /* Reset the read pointer, and read the status and packet length. */ - outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER); - rx_status = inw(ioaddr + DATA_1); - packet_length = inw(ioaddr + DATA_1) & 0x07ff; - - netdev_dbg(dev, "Receive status %4.4x length %d.\n", - rx_status, packet_length); - - if (!(rx_status & RS_ERRORS)) { - /* do stuff to make a new packet */ - struct sk_buff *skb; - struct smc_private *smc = netdev_priv(dev); - - /* Note: packet_length adds 5 or 6 extra bytes here! */ - skb = netdev_alloc_skb(dev, packet_length+2); - - if (skb == NULL) { - netdev_dbg(dev, "Low memory, packet dropped.\n"); - dev->stats.rx_dropped++; - outw(MC_RELEASE, ioaddr + MMU_CMD); - return; - } - - packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6); - skb_reserve(skb, 2); - insw(ioaddr+DATA_1, skb_put(skb, packet_length), - (packet_length+1)>>1); - skb->protocol = eth_type_trans(skb, dev); - - netif_rx(skb); - smc->last_rx = jiffies; - dev->stats.rx_packets++; - dev->stats.rx_bytes += packet_length; - if (rx_status & RS_MULTICAST) - dev->stats.multicast++; - } else { - /* error ... */ - dev->stats.rx_errors++; - - if (rx_status & RS_ALGNERR) dev->stats.rx_frame_errors++; - if (rx_status & (RS_TOOSHORT | RS_TOOLONG)) - dev->stats.rx_length_errors++; - if (rx_status & RS_BADCRC) dev->stats.rx_crc_errors++; - } - /* Let the MMU free the memory of this packet. */ - outw(MC_RELEASE, ioaddr + MMU_CMD); -} - -/*====================================================================== - - Set the receive mode. - - This routine is used by both the protocol level to notify us of - promiscuous/multicast mode changes, and by the open/reset code to - initialize the Rx registers. We always set the multicast list and - leave the receiver running. - -======================================================================*/ - -static void set_rx_mode(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct smc_private *smc = netdev_priv(dev); - unsigned char multicast_table[8]; - unsigned long flags; - u_short rx_cfg_setting; - int i; - - memset(multicast_table, 0, sizeof(multicast_table)); - - if (dev->flags & IFF_PROMISC) { - rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti; - } else if (dev->flags & IFF_ALLMULTI) - rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; - else { - if (!netdev_mc_empty(dev)) { - struct netdev_hw_addr *ha; - - netdev_for_each_mc_addr(ha, dev) { - u_int position = ether_crc(6, ha->addr); - multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); - } - } - rx_cfg_setting = RxStripCRC | RxEnable; - } - - /* Load MC table and Rx setting into the chip without interrupts. */ - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - for (i = 0; i < 8; i++) - outb(multicast_table[i], ioaddr + MULTICAST0 + i); - SMC_SELECT_BANK(0); - outw(rx_cfg_setting, ioaddr + RCR); - SMC_SELECT_BANK(2); - spin_unlock_irqrestore(&smc->lock, flags); -} - -/*====================================================================== - - Senses when a card's config changes. Here, it's coax or TP. - -======================================================================*/ - -static int s9k_config(struct net_device *dev, struct ifmap *map) -{ - struct smc_private *smc = netdev_priv(dev); - if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { - if (smc->cfg & CFG_MII_SELECT) - return -EOPNOTSUPP; - else if (map->port > 2) - return -EINVAL; - WRITE_ONCE(dev->if_port, map->port); - netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]); - smc_reset(dev); - } - return 0; -} - -/*====================================================================== - - Reset the chip, reloading every register that might be corrupted. - -======================================================================*/ - -/* - Set transceiver type, perhaps to something other than what the user - specified in dev->if_port. -*/ -static void smc_set_xcvr(struct net_device *dev, int if_port) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u_short saved_bank; - - saved_bank = inw(ioaddr + BANK_SELECT); - SMC_SELECT_BANK(1); - if (if_port == 2) { - outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) - set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); - smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002); - } else { - outw(smc->cfg, ioaddr + CONFIG); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) - mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); - smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001); - } - SMC_SELECT_BANK(saved_bank); -} - -static void smc_reset(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct smc_private *smc = netdev_priv(dev); - int i; - - netdev_dbg(dev, "smc91c92 reset called.\n"); - - /* The first interaction must be a write to bring the chip out - of sleep mode. */ - SMC_SELECT_BANK(0); - /* Reset the chip. */ - outw(RCR_SOFTRESET, ioaddr + RCR); - udelay(10); - - /* Clear the transmit and receive configuration registers. */ - outw(RCR_CLEAR, ioaddr + RCR); - outw(TCR_CLEAR, ioaddr + TCR); - - /* Set the Window 1 control, configuration and station addr registers. - No point in writing the I/O base register ;-> */ - SMC_SELECT_BANK(1); - /* Automatically release successfully transmitted packets, - Accept link errors, counter and Tx error interrupts. */ - outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, - ioaddr + CONTROL); - smc_set_xcvr(dev, dev->if_port); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) - outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) | - (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00), - ioaddr - 0x10 + OSITECH_AUI_PWR); - - /* Fill in the physical address. The databook is wrong about the order! */ - for (i = 0; i < 6; i += 2) - outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i], - ioaddr + ADDR0 + i); - - /* Reset the MMU */ - SMC_SELECT_BANK(2); - outw(MC_RESET, ioaddr + MMU_CMD); - outw(0, ioaddr + INTERRUPT); - - /* Re-enable the chip. */ - SMC_SELECT_BANK(0); - outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) | - TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR); - set_rx_mode(dev); - - if (smc->cfg & CFG_MII_SELECT) { - SMC_SELECT_BANK(3); - - /* Reset MII */ - mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000); - - /* Advertise 100F, 100H, 10F, 10H */ - mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1); - - /* Restart MII autonegotiation */ - mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000); - mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200); - } - - /* Enable interrupts. */ - SMC_SELECT_BANK(2); - outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8, - ioaddr + INTERRUPT); -} - -/*====================================================================== - - Media selection timer routine - -======================================================================*/ - -static void media_check(struct timer_list *t) -{ - struct smc_private *smc = timer_container_of(smc, t, media); - struct net_device *dev = smc->mii_if.dev; - unsigned int ioaddr = dev->base_addr; - u_short i, media, saved_bank; - u_short link; - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - - saved_bank = inw(ioaddr + BANK_SELECT); - - if (!netif_device_present(dev)) - goto reschedule; - - SMC_SELECT_BANK(2); - - /* need MC_RESET to keep the memory consistent. errata? */ - if (smc->rx_ovrn) { - outw(MC_RESET, ioaddr + MMU_CMD); - smc->rx_ovrn = 0; - } - i = inw(ioaddr + INTERRUPT); - SMC_SELECT_BANK(0); - media = inw(ioaddr + EPH) & EPH_LINK_OK; - SMC_SELECT_BANK(1); - media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1; - - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - - /* Check for pending interrupt with watchdog flag set: with - this, we can limp along even if the interrupt is blocked */ - if (smc->watchdog++ && ((i>>8) & i)) { - if (!smc->fast_poll) - netdev_info(dev, "interrupt(s) dropped!\n"); - local_irq_save(flags); - smc_interrupt(dev->irq, dev); - local_irq_restore(flags); - smc->fast_poll = HZ; - } - if (smc->fast_poll) { - smc->fast_poll--; - smc->media.expires = jiffies + HZ/100; - add_timer(&smc->media); - return; - } - - spin_lock_irqsave(&smc->lock, flags); - - saved_bank = inw(ioaddr + BANK_SELECT); - - if (smc->cfg & CFG_MII_SELECT) { - if (smc->mii_if.phy_id < 0) - goto reschedule; - - SMC_SELECT_BANK(3); - link = mdio_read(dev, smc->mii_if.phy_id, 1); - if (!link || (link == 0xffff)) { - netdev_info(dev, "MII is missing!\n"); - smc->mii_if.phy_id = -1; - goto reschedule; - } - - link &= 0x0004; - if (link != smc->link_status) { - u_short p = mdio_read(dev, smc->mii_if.phy_id, 5); - netdev_info(dev, "%s link beat\n", link ? "found" : "lost"); - smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40)) - ? TCR_FDUPLX : 0); - if (link) { - netdev_info(dev, "autonegotiation complete: " - "%dbaseT-%cD selected\n", - (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H'); - } - SMC_SELECT_BANK(0); - outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR); - smc->link_status = link; - } - goto reschedule; - } - - /* Ignore collisions unless we've had no rx's recently */ - if (time_after(jiffies, smc->last_rx + HZ)) { - if (smc->tx_err || (smc->media_status & EPH_16COL)) - media |= EPH_16COL; - } - smc->tx_err = 0; - - if (media != smc->media_status) { - if ((media & smc->media_status & 1) && - ((smc->media_status ^ media) & EPH_LINK_OK)) - netdev_info(dev, "%s link beat\n", - smc->media_status & EPH_LINK_OK ? "lost" : "found"); - else if ((media & smc->media_status & 2) && - ((smc->media_status ^ media) & EPH_16COL)) - netdev_info(dev, "coax cable %s\n", - media & EPH_16COL ? "problem" : "ok"); - if (dev->if_port == 0) { - if (media & 1) { - if (media & EPH_LINK_OK) - netdev_info(dev, "flipped to 10baseT\n"); - else - smc_set_xcvr(dev, 2); - } else { - if (media & EPH_16COL) - smc_set_xcvr(dev, 1); - else - netdev_info(dev, "flipped to 10base2\n"); - } - } - smc->media_status = media; - } - -reschedule: - smc->media.expires = jiffies + HZ; - add_timer(&smc->media); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); -} - -static int smc_link_ok(struct net_device *dev) -{ - unsigned int ioaddr = dev->base_addr; - struct smc_private *smc = netdev_priv(dev); - - if (smc->cfg & CFG_MII_SELECT) { - return mii_link_ok(&smc->mii_if); - } else { - SMC_SELECT_BANK(0); - return inw(ioaddr + EPH) & EPH_LINK_OK; - } -} - -static void smc_netdev_get_ecmd(struct net_device *dev, - struct ethtool_link_ksettings *ecmd) -{ - u16 tmp; - unsigned int ioaddr = dev->base_addr; - u32 supported; - - supported = (SUPPORTED_TP | SUPPORTED_AUI | - SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); - - SMC_SELECT_BANK(1); - tmp = inw(ioaddr + CONFIG); - ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP; - ecmd->base.speed = SPEED_10; - ecmd->base.phy_address = ioaddr + MGMT; - - SMC_SELECT_BANK(0); - tmp = inw(ioaddr + TCR); - ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF; - - ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported, - supported); -} - -static int smc_netdev_set_ecmd(struct net_device *dev, - const struct ethtool_link_ksettings *ecmd) -{ - u16 tmp; - unsigned int ioaddr = dev->base_addr; - - if (ecmd->base.speed != SPEED_10) - return -EINVAL; - if (ecmd->base.duplex != DUPLEX_HALF && - ecmd->base.duplex != DUPLEX_FULL) - return -EINVAL; - if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI) - return -EINVAL; - - if (ecmd->base.port == PORT_AUI) - smc_set_xcvr(dev, 1); - else - smc_set_xcvr(dev, 0); - - SMC_SELECT_BANK(0); - tmp = inw(ioaddr + TCR); - if (ecmd->base.duplex == DUPLEX_FULL) - tmp |= TCR_FDUPLX; - else - tmp &= ~TCR_FDUPLX; - outw(tmp, ioaddr + TCR); - - return 0; -} - -static int check_if_running(struct net_device *dev) -{ - if (!netif_running(dev)) - return -EINVAL; - return 0; -} - -static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) -{ - strscpy(info->driver, DRV_NAME, sizeof(info->driver)); - strscpy(info->version, DRV_VERSION, sizeof(info->version)); -} - -static int smc_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings *ecmd) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - if (smc->cfg & CFG_MII_SELECT) - mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd); - else - smc_netdev_get_ecmd(dev, ecmd); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return 0; -} - -static int smc_set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings *ecmd) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - int ret; - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - if (smc->cfg & CFG_MII_SELECT) - ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd); - else - ret = smc_netdev_set_ecmd(dev, ecmd); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return ret; -} - -static u32 smc_get_link(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - u32 ret; - unsigned long flags; - - spin_lock_irqsave(&smc->lock, flags); - SMC_SELECT_BANK(3); - ret = smc_link_ok(dev); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return ret; -} - -static int smc_nway_reset(struct net_device *dev) -{ - struct smc_private *smc = netdev_priv(dev); - if (smc->cfg & CFG_MII_SELECT) { - unsigned int ioaddr = dev->base_addr; - u16 saved_bank = inw(ioaddr + BANK_SELECT); - int res; - - SMC_SELECT_BANK(3); - res = mii_nway_restart(&smc->mii_if); - SMC_SELECT_BANK(saved_bank); - - return res; - } else - return -EOPNOTSUPP; -} - -static const struct ethtool_ops ethtool_ops = { - .begin = check_if_running, - .get_drvinfo = smc_get_drvinfo, - .get_link = smc_get_link, - .nway_reset = smc_nway_reset, - .get_link_ksettings = smc_get_link_ksettings, - .set_link_ksettings = smc_set_link_ksettings, -}; - -static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) -{ - struct smc_private *smc = netdev_priv(dev); - struct mii_ioctl_data *mii = if_mii(rq); - int rc = 0; - u16 saved_bank; - unsigned int ioaddr = dev->base_addr; - unsigned long flags; - - if (!netif_running(dev)) - return -EINVAL; - - spin_lock_irqsave(&smc->lock, flags); - saved_bank = inw(ioaddr + BANK_SELECT); - SMC_SELECT_BANK(3); - rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL); - SMC_SELECT_BANK(saved_bank); - spin_unlock_irqrestore(&smc->lock, flags); - return rc; -} - -static const struct pcmcia_device_id smc91c92_ids[] = { - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501), - PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), - PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), - PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020), - PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023), - PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb), - PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc), - PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1), - PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5), - PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9), - PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953), - PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a), - PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314), - PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a), - PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc), - PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9), - PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d), - /* These conflict with other cards! */ - /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */ - /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */ - PCMCIA_DEVICE_NULL, -}; -MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids); - -static struct pcmcia_driver smc91c92_cs_driver = { - .owner = THIS_MODULE, - .name = "smc91c92_cs", - .probe = smc91c92_probe, - .remove = smc91c92_detach, - .id_table = smc91c92_ids, - .suspend = smc91c92_suspend, - .resume = smc91c92_resume, -}; -module_pcmcia_driver(smc91c92_cs_driver); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c index e2240b68ad98..2ab6ecac6422 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-nuvoton.c @@ -100,6 +100,8 @@ static int nvt_gmac_probe(struct platform_device *pdev) if (!priv) return dev_err_probe(dev, -ENOMEM, "Failed to allocate private data\n"); + priv->dev = dev; + priv->regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "nuvoton,sys", 1, &priv->macid); if (IS_ERR(priv->regmap)) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 01a983001ab4..3591755ea30b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1410,8 +1410,6 @@ static int stmmac_phylink_setup(struct stmmac_priv *priv) priv->tx_lpi_clk_stop = priv->plat->flags & STMMAC_FLAG_EN_TX_LPI_CLOCKGATING; - config->default_an_inband = priv->plat->default_an_inband; - /* Get the PHY interface modes (at the PHY end of the link) that * are supported by the platform. */ @@ -1419,6 +1417,8 @@ static int stmmac_phylink_setup(struct stmmac_priv *priv) priv->plat->get_interfaces(priv, priv->plat->bsp_priv, config->supported_interfaces); + config->default_an_inband = priv->plat->default_an_inband; + /* Set the platform/firmware specified interface mode if the * supported interfaces have not already been provided using * phy_interface as a last resort. @@ -5549,9 +5549,12 @@ read_again: break; /* Prefetch the next RX descriptor */ - rx_q->cur_rx = STMMAC_NEXT_ENTRY(rx_q->cur_rx, - priv->dma_conf.dma_rx_size); - next_entry = rx_q->cur_rx; + next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx, + priv->dma_conf.dma_rx_size); + if (unlikely(next_entry == rx_q->dirty_rx)) + break; + + rx_q->cur_rx = next_entry; np = stmmac_get_rx_desc(priv, rx_q, next_entry); @@ -5686,7 +5689,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) dma_dir = page_pool_get_dma_dir(rx_q->page_pool); bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; - limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit); if (netif_msg_rx_status(priv)) { void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0); @@ -5733,9 +5735,12 @@ read_again: if (unlikely(status & dma_own)) break; - rx_q->cur_rx = STMMAC_NEXT_ENTRY(rx_q->cur_rx, - priv->dma_conf.dma_rx_size); - next_entry = rx_q->cur_rx; + next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx, + priv->dma_conf.dma_rx_size); + if (unlikely(next_entry == rx_q->dirty_rx)) + break; + + rx_q->cur_rx = next_entry; np = stmmac_get_rx_desc(priv, rx_q, next_entry); diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c index 53bbd9290904..b7e94244355a 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -1825,6 +1825,7 @@ static int icssm_prueth_probe(struct platform_device *pdev) dev_err(dev, "%pOF error reading port_id %d\n", eth_node, ret); of_node_put(eth_node); + of_node_put(eth_ports_node); return ret; } diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index d3772d01e00b..2451f6b20b11 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -2480,8 +2480,11 @@ int wx_sw_init(struct wx *wx) wx->oem_svid = pdev->subsystem_vendor; wx->oem_ssid = pdev->subsystem_device; wx->bus.device = PCI_SLOT(pdev->devfn); - wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID, - rd32(wx, WX_CFG_PORT_ST)); + if (pdev->is_virtfn) + wx->bus.func = PCI_FUNC(pdev->devfn); + else + wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID, + rd32(wx, WX_CFG_PORT_ST)); if (wx->oem_svid == PCI_VENDOR_ID_WANGXUN || pdev->is_virtfn) { diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c index 29cdbed2e5ec..94ff8f5f0b4c 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c @@ -99,8 +99,8 @@ int wx_request_msix_irqs_vf(struct wx *wx) } } - err = request_threaded_irq(wx->msix_entry->vector, wx_msix_misc_vf, - NULL, IRQF_ONESHOT, netdev->name, wx); + err = request_irq(wx->msix_entry->vector, wx_msix_misc_vf, + 0, netdev->name, wx); if (err) { wx_err(wx, "request_irq for msix_other failed: %d\n", err); goto free_queue_irqs; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index ec32a5f422f2..8b7c3753bb6a 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -864,7 +864,8 @@ static int txgbe_probe(struct pci_dev *pdev, "0x%08x", etrack_id); } - if (etrack_id < 0x20010) + if (wx->mac.type == wx_mac_sp && + ((etrack_id & 0xfffff) < 0x20010)) dev_warn(&pdev->dev, "Please upgrade the firmware to 0x20010 or above.\n"); err = txgbe_test_hostif(wx); diff --git a/drivers/net/fddi/defza.c b/drivers/net/fddi/defza.c index 064fa484f797..9bfecc87d6b2 100644 --- a/drivers/net/fddi/defza.c +++ b/drivers/net/fddi/defza.c @@ -984,7 +984,7 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) case FZA_STATE_UNINITIALIZED: netif_carrier_off(dev); - timer_delete_sync(&fp->reset_timer); + timer_delete_sync_try(&fp->reset_timer); fp->ring_cmd_index = 0; fp->ring_uns_index = 0; fp->ring_rmc_tx_index = 0; @@ -1018,7 +1018,9 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) fp->queue_active = 0; netif_stop_queue(dev); pr_debug("%s: queue stopped\n", fp->name); - timer_delete_sync(&fp->reset_timer); + + spin_lock(&fp->lock); + timer_delete(&fp->reset_timer); pr_warn("%s: halted, reason: %x\n", fp->name, FZA_STATUS_GET_HALT(status)); fza_regs_dump(fp); @@ -1027,6 +1029,8 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) fp->timer_state = 0; fp->reset_timer.expires = jiffies + 45 * HZ; add_timer(&fp->reset_timer); + spin_unlock(&fp->lock); + break; default: @@ -1046,7 +1050,9 @@ static irqreturn_t fza_interrupt(int irq, void *dev_id) static void fza_reset_timer(struct timer_list *t) { struct fza_private *fp = timer_container_of(fp, t, reset_timer); + unsigned long flags; + spin_lock_irqsave(&fp->lock, flags); if (!fp->timer_state) { pr_err("%s: RESET timed out!\n", fp->name); pr_info("%s: trying harder...\n", fp->name); @@ -1069,6 +1075,7 @@ static void fza_reset_timer(struct timer_list *t) fp->reset_timer.expires = jiffies + 45 * HZ; } add_timer(&fp->reset_timer); + spin_unlock_irqrestore(&fp->lock, flags); } static int fza_set_mac_address(struct net_device *dev, void *addr) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 70b9e58b9b78..5150f2e4f66b 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -2400,6 +2400,7 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) return -ENODEV; } + local_bh_disable(); udp_tunnel_xmit_skb(rt, sk, skb_to_send, fl4.saddr, fl4.daddr, inet_dscp_to_dsfield(fl4.flowi4_dscp), @@ -2409,6 +2410,7 @@ static int gtp_genl_send_echo_req(struct sk_buff *skb, struct genl_info *info) !net_eq(sock_net(sk), dev_net(gtp->dev)), false, 0); + local_bh_enable(); return 0; } diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c deleted file mode 100644 index c8b2dc5c1bec..000000000000 --- a/drivers/net/hamradio/6pack.c +++ /dev/null @@ -1,912 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * 6pack.c This module implements the 6pack protocol for kernel-based - * devices like TTY. It interfaces between a raw TTY and the - * kernel's AX.25 protocol layers. - * - * Authors: Andreas Könsgen <ajk@comnets.uni-bremen.de> - * Ralf Baechle DL5RB <ralf@linux-mips.org> - * - * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by - * - * Laurence Culhane, <loz@holmes.demon.co.uk> - * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> - */ - -#include <linux/module.h> -#include <linux/uaccess.h> -#include <linux/bitops.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/tty.h> -#include <linux/errno.h> -#include <linux/netdevice.h> -#include <linux/timer.h> -#include <linux/slab.h> -#include <net/ax25.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/rtnetlink.h> -#include <linux/spinlock.h> -#include <linux/if_arp.h> -#include <linux/init.h> -#include <linux/ip.h> -#include <linux/tcp.h> -#include <linux/semaphore.h> -#include <linux/refcount.h> - -/* sixpack priority commands */ -#define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ -#define SIXP_TX_URUN 0x48 /* transmit overrun */ -#define SIXP_RX_ORUN 0x50 /* receive overrun */ -#define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */ - -#define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */ - -/* masks to get certain bits out of the status bytes sent by the TNC */ - -#define SIXP_CMD_MASK 0xC0 -#define SIXP_CHN_MASK 0x07 -#define SIXP_PRIO_CMD_MASK 0x80 -#define SIXP_STD_CMD_MASK 0x40 -#define SIXP_PRIO_DATA_MASK 0x38 -#define SIXP_TX_MASK 0x20 -#define SIXP_RX_MASK 0x10 -#define SIXP_RX_DCD_MASK 0x18 -#define SIXP_LEDS_ON 0x78 -#define SIXP_LEDS_OFF 0x60 -#define SIXP_CON 0x08 -#define SIXP_STA 0x10 - -#define SIXP_FOUND_TNC 0xe9 -#define SIXP_CON_ON 0x68 -#define SIXP_DCD_MASK 0x08 -#define SIXP_DAMA_OFF 0 - -/* default level 2 parameters */ -#define SIXP_TXDELAY 25 /* 250 ms */ -#define SIXP_PERSIST 50 /* in 256ths */ -#define SIXP_SLOTTIME 10 /* 100 ms */ -#define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */ -#define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */ - -/* 6pack configuration. */ -#define SIXP_NRUNIT 31 /* MAX number of 6pack channels */ -#define SIXP_MTU 256 /* Default MTU */ - -enum sixpack_flags { - SIXPF_ERROR, /* Parity, etc. error */ -}; - -struct sixpack { - /* Various fields. */ - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - - /* These are pointers to the malloc()ed frame buffers. */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - u8 raw_buf[4]; - u8 cooked_buf[400]; - - unsigned int rx_count; - unsigned int rx_count_cooked; - spinlock_t rxlock; - - unsigned long flags; /* Flag values/ mode etc */ - unsigned char mode; /* 6pack mode */ - - /* 6pack stuff */ - unsigned char tx_delay; - unsigned char persistence; - unsigned char slottime; - unsigned char duplex; - unsigned char led_state; - u8 status; - u8 status1; - unsigned char status2; - unsigned char tx_enable; - unsigned char tnc_state; - - struct timer_list tx_t; - struct timer_list resync_t; - spinlock_t lock; -}; - -#define AX25_6PACK_HEADER_LEN 0 - -static void sixpack_decode(struct sixpack *, const u8 *, size_t); -static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); - -/* - * Perform the persistence/slottime algorithm for CSMA access. If the - * persistence check was successful, write the data to the serial driver. - * Note that in case of DAMA operation, the data is not sent here. - */ - -static void sp_xmit_on_air(struct timer_list *t) -{ - struct sixpack *sp = timer_container_of(sp, t, tx_t); - int actual, when = sp->slottime; - static unsigned char random; - - random = random * 17 + 41; - - if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistence)) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); - sp->xleft -= actual; - sp->xhead += actual; - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->status2 = 0; - } else - mod_timer(&sp->tx_t, jiffies + ((when + 1) * HZ) / 100); -} - -/* ----> 6pack timer interrupt handler and friends. <---- */ - -/* Encapsulate one AX.25 frame and stuff into a TTY queue. */ -static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) -{ - unsigned char *msg, *p = icp; - int actual, count; - - if (len > AX25_MTU + 73) { - msg = "oversized transmit packet!"; - goto out_drop; - } - - if (p[0] > 5) { - msg = "invalid KISS command"; - goto out_drop; - } - - if ((p[0] != 0) && (len > 2)) { - msg = "KISS control packet too long"; - goto out_drop; - } - - if ((p[0] == 0) && (len < 15)) { - msg = "bad AX.25 packet to transmit"; - goto out_drop; - } - - count = encode_sixpack(p, sp->xbuff, len, sp->tx_delay); - set_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags); - - switch (p[0]) { - case 1: sp->tx_delay = p[1]; - return; - case 2: sp->persistence = p[1]; - return; - case 3: sp->slottime = p[1]; - return; - case 4: /* ignored */ - return; - case 5: sp->duplex = p[1]; - return; - } - - if (p[0] != 0) - return; - - /* - * In case of fullduplex or DAMA operation, we don't take care about the - * state of the DCD or of any timers, as the determination of the - * correct time to send is the job of the AX.25 layer. We send - * immediately after data has arrived. - */ - if (sp->duplex == 1) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, count); - sp->xleft = count - actual; - sp->xhead = sp->xbuff + actual; - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - } else { - sp->xleft = count; - sp->xhead = sp->xbuff; - sp->status2 = count; - sp_xmit_on_air(&sp->tx_t); - } - - return; - -out_drop: - sp->dev->stats.tx_dropped++; - netif_start_queue(sp->dev); - if (net_ratelimit()) - printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg); -} - -/* Encapsulate an IP datagram and kick it into a TTY queue. */ - -static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - spin_lock_bh(&sp->lock); - /* We were not busy, so we are now... :-) */ - netif_stop_queue(dev); - dev->stats.tx_bytes += skb->len; - sp_encaps(sp, skb->data, skb->len); - spin_unlock_bh(&sp->lock); - - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static int sp_open_dev(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - if (sp->tty == NULL) - return -ENODEV; - return 0; -} - -/* Close the low-level part of the 6pack channel. */ -static int sp_close(struct net_device *dev) -{ - struct sixpack *sp = netdev_priv(dev); - - spin_lock_bh(&sp->lock); - if (sp->tty) { - /* TTY discipline is running. */ - clear_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags); - } - netif_stop_queue(dev); - spin_unlock_bh(&sp->lock); - - return 0; -} - -static int sp_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr_ax25 *sa = addr; - - netif_tx_lock_bh(dev); - netif_addr_lock(dev); - __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN); - netif_addr_unlock(dev); - netif_tx_unlock_bh(dev); - - return 0; -} - -static const struct net_device_ops sp_netdev_ops = { - .ndo_open = sp_open_dev, - .ndo_stop = sp_close, - .ndo_start_xmit = sp_xmit, - .ndo_set_mac_address = sp_set_mac_address, -}; - -static void sp_setup(struct net_device *dev) -{ - /* Finish setting up the DEVICE info. */ - dev->netdev_ops = &sp_netdev_ops; - dev->mtu = SIXP_MTU; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->header_ops = &ax25_header_ops; - - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_AX25; - dev->tx_queue_len = 10; - - /* Only activated in AX.25 mode */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - - dev->flags = 0; -} - -/* Send one completely decapsulated IP datagram to the IP layer. */ - -/* - * This is the routine that sends the received data to the kernel AX.25. - * 'cmd' is the KISS command. For AX.25 data, it is zero. - */ - -static void sp_bump(struct sixpack *sp, char cmd) -{ - struct sk_buff *skb; - int count; - u8 *ptr; - - count = sp->rcount + 1; - - sp->dev->stats.rx_bytes += count; - - if ((skb = dev_alloc_skb(count + 1)) == NULL) - goto out_mem; - - ptr = skb_put(skb, count + 1); - *ptr++ = cmd; /* KISS command */ - - memcpy(ptr, sp->cooked_buf + 1, count); - skb->protocol = ax25_type_trans(skb, sp->dev); - netif_rx(skb); - sp->dev->stats.rx_packets++; - - return; - -out_mem: - sp->dev->stats.rx_dropped++; -} - - -/* ----------------------------------------------------------------------- */ - -/* - * Called by the TTY driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void sixpack_write_wakeup(struct tty_struct *tty) -{ - struct sixpack *sp = tty->disc_data; - int actual; - - if (!sp) - return; - if (sp->xleft <= 0) { - /* Now serial buffer is almost free & we can start - * transmission of another packet */ - sp->dev->stats.tx_packets++; - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - sp->tx_enable = 0; - netif_wake_queue(sp->dev); - return; - } - - if (sp->tx_enable) { - actual = tty->ops->write(tty, sp->xhead, sp->xleft); - sp->xleft -= actual; - sp->xhead += actual; - } -} - -/* ----------------------------------------------------------------------- */ - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the tty module in the kernel when - * a block of 6pack data has been received, which can now be decapsulated - * and sent on to some IP layer for further processing. - */ -static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp, - const u8 *fp, size_t count) -{ - struct sixpack *sp; - - if (!count) - return; - - sp = tty->disc_data; - if (!sp) - return; - - /* Read the characters out of the buffer */ - while (count--) { - if (fp && *fp++) { - if (!test_and_set_bit(SIXPF_ERROR, &sp->flags)) - sp->dev->stats.rx_errors++; - cp++; - continue; - } - sixpack_decode(sp, cp, 1); - cp++; - } - - tty_unthrottle(tty); -} - -/* - * Try to resync the TNC. Called by the resync timer defined in - * decode_prio_command - */ - -#define TNC_UNINITIALIZED 0 -#define TNC_UNSYNC_STARTUP 1 -#define TNC_UNSYNCED 2 -#define TNC_IN_SYNC 3 - -static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) -{ - char *msg; - - switch (new_tnc_state) { - default: /* gcc oh piece-o-crap ... */ - case TNC_UNSYNC_STARTUP: - msg = "Synchronizing with TNC"; - break; - case TNC_UNSYNCED: - msg = "Lost synchronization with TNC\n"; - break; - case TNC_IN_SYNC: - msg = "Found TNC"; - break; - } - - sp->tnc_state = new_tnc_state; - printk(KERN_INFO "%s: %s\n", sp->dev->name, msg); -} - -static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) -{ - int old_tnc_state = sp->tnc_state; - - if (old_tnc_state != new_tnc_state) - __tnc_set_sync_state(sp, new_tnc_state); -} - -static void resync_tnc(struct timer_list *t) -{ - struct sixpack *sp = timer_container_of(sp, t, resync_t); - static char resync_cmd = 0xe8; - - /* clear any data that might have been received */ - - sp->rx_count = 0; - sp->rx_count_cooked = 0; - - /* reset state machine */ - - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - - /* resync the TNC */ - - sp->led_state = 0x60; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tty->ops->write(sp->tty, &resync_cmd, 1); - - - /* Start resync timer again -- the TNC might be still absent */ - mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); -} - -static inline int tnc_init(struct sixpack *sp) -{ - unsigned char inbyte = 0xe8; - - tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP); - - sp->tty->ops->write(sp->tty, &inbyte, 1); - - mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT); - - return 0; -} - -/* - * Open the high-level part of the 6pack channel. - * This function is called by the TTY module when the - * 6pack line discipline is called for. Because we are - * sure the tty line exists, we only have to link it to - * a free 6pcack channel... - */ -static int sixpack_open(struct tty_struct *tty) -{ - char *xbuff = NULL; - struct net_device *dev; - struct sixpack *sp; - unsigned long len; - int err = 0; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - - dev = alloc_netdev(sizeof(struct sixpack), "sp%d", NET_NAME_UNKNOWN, - sp_setup); - if (!dev) { - err = -ENOMEM; - goto out; - } - - sp = netdev_priv(dev); - sp->dev = dev; - - spin_lock_init(&sp->lock); - spin_lock_init(&sp->rxlock); - - /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */ - - len = dev->mtu * 2; - - xbuff = kmalloc(len + 4, GFP_KERNEL); - if (xbuff == NULL) { - err = -ENOBUFS; - goto out_free; - } - - spin_lock_bh(&sp->lock); - - sp->tty = tty; - - sp->xbuff = xbuff; - - sp->rcount = 0; - sp->rx_count = 0; - sp->rx_count_cooked = 0; - sp->xleft = 0; - - sp->flags = 0; /* Clear ESCAPE & ERROR flags */ - - sp->duplex = 0; - sp->tx_delay = SIXP_TXDELAY; - sp->persistence = SIXP_PERSIST; - sp->slottime = SIXP_SLOTTIME; - sp->led_state = 0x60; - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - sp->tx_enable = 0; - - netif_start_queue(dev); - - timer_setup(&sp->tx_t, sp_xmit_on_air, 0); - - timer_setup(&sp->resync_t, resync_tnc, 0); - - spin_unlock_bh(&sp->lock); - - /* Done. We have linked the TTY line to a channel. */ - tty->disc_data = sp; - tty->receive_room = 65536; - - /* Now we're ready to register. */ - err = register_netdev(dev); - if (err) - goto out_free; - - tnc_init(sp); - - return 0; - -out_free: - kfree(xbuff); - - free_netdev(dev); - -out: - return err; -} - - -/* - * Close down a 6pack channel. - * This means flushing out any pending queues, and then restoring the - * TTY line discipline to what it was before it got hooked to 6pack - * (which usually is TTY again). - */ -static void sixpack_close(struct tty_struct *tty) -{ - struct sixpack *sp; - - sp = tty->disc_data; - if (!sp) - return; - - tty->disc_data = NULL; - - /* We must stop the queue to avoid potentially scribbling - * on the free buffers. The sp->dead completion is not sufficient - * to protect us from sp->xbuff access. - */ - netif_stop_queue(sp->dev); - - unregister_netdev(sp->dev); - - timer_delete_sync(&sp->tx_t); - timer_delete_sync(&sp->resync_t); - - /* Free all 6pack frame buffers after unreg. */ - kfree(sp->xbuff); - - free_netdev(sp->dev); -} - -/* Perform I/O control on an active 6pack channel. */ -static int sixpack_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct sixpack *sp = tty->disc_data; - struct net_device *dev; - unsigned int tmp, err; - - if (!sp) - return -ENXIO; - dev = sp->dev; - - switch(cmd) { - case SIOCGIFNAME: - err = copy_to_user((void __user *) arg, dev->name, - strlen(dev->name) + 1) ? -EFAULT : 0; - break; - - case SIOCGIFENCAP: - err = put_user(0, (int __user *) arg); - break; - - case SIOCSIFENCAP: - if (get_user(tmp, (int __user *) arg)) { - err = -EFAULT; - break; - } - - sp->mode = tmp; - dev->addr_len = AX25_ADDR_LEN; - dev->hard_header_len = AX25_KISS_HEADER_LEN + - AX25_MAX_HEADER_LEN + 3; - dev->type = ARPHRD_AX25; - - err = 0; - break; - - case SIOCSIFHWADDR: { - char addr[AX25_ADDR_LEN]; - - if (copy_from_user(&addr, - (void __user *)arg, AX25_ADDR_LEN)) { - err = -EFAULT; - break; - } - - netif_tx_lock_bh(dev); - __dev_addr_set(dev, &addr, AX25_ADDR_LEN); - netif_tx_unlock_bh(dev); - err = 0; - break; - } - default: - err = tty_mode_ioctl(tty, cmd, arg); - } - - return err; -} - -static struct tty_ldisc_ops sp_ldisc = { - .owner = THIS_MODULE, - .num = N_6PACK, - .name = "6pack", - .open = sixpack_open, - .close = sixpack_close, - .ioctl = sixpack_ioctl, - .receive_buf = sixpack_receive_buf, - .write_wakeup = sixpack_write_wakeup, -}; - -/* Initialize 6pack control device -- register 6pack line discipline */ - -static int __init sixpack_init_driver(void) -{ - int status; - - /* Register the provided line protocol discipline */ - status = tty_register_ldisc(&sp_ldisc); - if (status) - pr_err("6pack: can't register line discipline (err = %d)\n", status); - - return status; -} - -static void __exit sixpack_exit_driver(void) -{ - tty_unregister_ldisc(&sp_ldisc); -} - -/* encode an AX.25 packet into 6pack */ - -static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, - int length, unsigned char tx_delay) -{ - int count = 0; - unsigned char checksum = 0, buf[400]; - int raw_count = 0; - - tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK; - tx_buf_raw[raw_count++] = SIXP_SEOF; - - buf[0] = tx_delay; - for (count = 1; count < length; count++) - buf[count] = tx_buf[count]; - - for (count = 0; count < length; count++) - checksum += buf[count]; - buf[length] = (unsigned char) 0xff - checksum; - - for (count = 0; count <= length; count++) { - if ((count % 3) == 0) { - tx_buf_raw[raw_count++] = (buf[count] & 0x3f); - tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30); - } else if ((count % 3) == 1) { - tx_buf_raw[raw_count++] |= (buf[count] & 0x0f); - tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c); - } else { - tx_buf_raw[raw_count++] |= (buf[count] & 0x03); - tx_buf_raw[raw_count++] = (buf[count] >> 2); - } - } - if ((length % 3) != 2) - raw_count++; - tx_buf_raw[raw_count++] = SIXP_SEOF; - return raw_count; -} - -/* decode 4 sixpack-encoded bytes into 3 data bytes */ - -static void decode_data(struct sixpack *sp, u8 inbyte) -{ - u8 *buf; - - if (sp->rx_count != 3) { - sp->raw_buf[sp->rx_count++] = inbyte; - - return; - } - - if (sp->rx_count_cooked + 2 >= sizeof(sp->cooked_buf)) { - pr_err("6pack: cooked buffer overrun, data loss\n"); - sp->rx_count = 0; - return; - } - - buf = sp->raw_buf; - sp->cooked_buf[sp->rx_count_cooked++] = - buf[0] | ((buf[1] << 2) & 0xc0); - sp->cooked_buf[sp->rx_count_cooked++] = - (buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0); - sp->cooked_buf[sp->rx_count_cooked++] = - (buf[2] & 0x03) | (inbyte << 2); - sp->rx_count = 0; -} - -/* identify and execute a 6pack priority command byte */ - -static void decode_prio_command(struct sixpack *sp, u8 cmd) -{ - ssize_t actual; - - if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ - - /* RX and DCD flags can only be set in the same prio command, - if the DCD flag has been set without the RX flag in the previous - prio command. If DCD has not been set before, something in the - transmission has gone wrong. In this case, RX and DCD are - cleared in order to prevent the decode_data routine from - reading further data that might be corrupt. */ - - if (((sp->status & SIXP_DCD_MASK) == 0) && - ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) { - if (sp->status != 1) - printk(KERN_DEBUG "6pack: protocol violation\n"); - else - sp->status = 0; - cmd &= ~SIXP_RX_DCD_MASK; - } - sp->status = cmd & SIXP_PRIO_DATA_MASK; - } else { /* output watchdog char if idle */ - if ((sp->status2 != 0) && (sp->duplex == 1)) { - sp->led_state = 0x70; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); - sp->xleft -= actual; - sp->xhead += actual; - sp->led_state = 0x60; - sp->status2 = 0; - - } - } - - /* needed to trigger the TNC watchdog */ - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - - /* if the state byte has been received, the TNC is present, - so the resync timer can be reset. */ - - if (sp->tnc_state == TNC_IN_SYNC) - mod_timer(&sp->resync_t, jiffies + SIXP_INIT_RESYNC_TIMEOUT); - - sp->status1 = cmd & SIXP_PRIO_DATA_MASK; -} - -/* identify and execute a standard 6pack command byte */ - -static void decode_std_command(struct sixpack *sp, u8 cmd) -{ - u8 checksum = 0, rest = 0; - short i; - - switch (cmd & SIXP_CMD_MASK) { /* normal command */ - case SIXP_SEOF: - if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) { - if ((sp->status & SIXP_RX_DCD_MASK) == - SIXP_RX_DCD_MASK) { - sp->led_state = 0x68; - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - } - } else { - sp->led_state = 0x60; - /* fill trailing bytes with zeroes */ - sp->tty->ops->write(sp->tty, &sp->led_state, 1); - spin_lock_bh(&sp->rxlock); - rest = sp->rx_count; - if (rest != 0) - for (i = rest; i <= 3; i++) - decode_data(sp, 0); - if (rest == 2) - sp->rx_count_cooked -= 2; - else if (rest == 3) - sp->rx_count_cooked -= 1; - for (i = 0; i < sp->rx_count_cooked; i++) - checksum += sp->cooked_buf[i]; - if (checksum != SIXP_CHKSUM) { - printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum); - } else { - sp->rcount = sp->rx_count_cooked-2; - sp_bump(sp, 0); - } - sp->rx_count_cooked = 0; - spin_unlock_bh(&sp->rxlock); - } - break; - case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n"); - break; - case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n"); - break; - case SIXP_RX_BUF_OVL: - printk(KERN_DEBUG "6pack: RX buffer overflow\n"); - } -} - -/* decode a 6pack packet */ - -static void -sixpack_decode(struct sixpack *sp, const u8 *pre_rbuff, size_t count) -{ - size_t count1; - u8 inbyte; - - for (count1 = 0; count1 < count; count1++) { - inbyte = pre_rbuff[count1]; - if (inbyte == SIXP_FOUND_TNC) { - tnc_set_sync_state(sp, TNC_IN_SYNC); - timer_delete(&sp->resync_t); - } - if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) - decode_prio_command(sp, inbyte); - else if ((inbyte & SIXP_STD_CMD_MASK) != 0) - decode_std_command(sp, inbyte); - else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) { - spin_lock_bh(&sp->rxlock); - decode_data(sp, inbyte); - spin_unlock_bh(&sp->rxlock); - } - } -} - -MODULE_AUTHOR("Ralf Baechle DO1GRB <ralf@linux-mips.org>"); -MODULE_DESCRIPTION("6pack driver for AX.25"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_6PACK); - -module_init(sixpack_init_driver); -module_exit(sixpack_exit_driver); diff --git a/drivers/net/hamradio/Kconfig b/drivers/net/hamradio/Kconfig deleted file mode 100644 index 36a9aade9f33..000000000000 --- a/drivers/net/hamradio/Kconfig +++ /dev/null @@ -1,162 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config MKISS - tristate "Serial port KISS driver" - depends on AX25 && TTY - select CRC16 - help - KISS is a protocol used for the exchange of data between a computer - and a Terminal Node Controller (a small embedded system commonly - used for networking over AX.25 amateur radio connections; it - connects the computer's serial port with the radio's microphone - input and speaker output). - - Although KISS is less advanced than the 6pack protocol, it has - the advantage that it is already supported by most modern TNCs - without the need for a firmware upgrade. - - To compile this driver as a module, choose M here: the module - will be called mkiss. - -config 6PACK - tristate "Serial port 6PACK driver" - depends on AX25 && TTY - help - 6pack is a transmission protocol for the data exchange between your - PC and your TNC (the Terminal Node Controller acts as a kind of - modem connecting your computer's serial port to your radio's - microphone input and speaker output). This protocol can be used as - an alternative to KISS for networking over AX.25 amateur radio - connections, but it has some extended functionality. - - Note that this driver is still experimental and might cause - problems. For details about the features and the usage of the - driver, read <file:Documentation/networking/6pack.rst>. - - To compile this driver as a module, choose M here: the module - will be called 6pack. - -config BPQETHER - tristate "BPQ Ethernet driver" - depends on AX25 - help - AX.25 is the protocol used for computer communication over amateur - radio. If you say Y here, you will be able to send and receive AX.25 - traffic over Ethernet (also called "BPQ AX.25"), which could be - useful if some other computer on your local network has a direct - amateur radio connection. - -config SCC - tristate "Z8530 SCC driver" - depends on ISA && AX25 - help - These cards are used to connect your Linux box to an amateur radio - in order to communicate with other computers. If you want to use - this, read - <file:Documentation/networking/device_drivers/hamradio/z8530drv.rst> - and the AX25-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. Also make sure to say Y - to "Amateur Radio AX.25 Level 2" support. - - To compile this driver as a module, choose M here: the module - will be called scc. - -config SCC_DELAY - bool "additional delay for PA0HZP OptoSCC compatible boards" - depends on SCC - help - Say Y here if you experience problems with the SCC driver not - working properly; please read - <file:Documentation/networking/device_drivers/hamradio/z8530drv.rst> - for details. - - If unsure, say N. - -config SCC_TRXECHO - bool "support for TRX that feedback the tx signal to rx" - depends on SCC - help - Some transmitters feed the transmitted signal back to the receive - line. Say Y here to foil this by explicitly disabling the receiver - during data transmission. - - If in doubt, say Y. - -config BAYCOM_SER_FDX - tristate "BAYCOM ser12 fullduplex driver for AX.25" - depends on AX25 && HAS_IOPORT - select CRC_CCITT - help - This is one of two drivers for Baycom style simple amateur radio - modems that connect to a serial interface. The driver supports the - ser12 design in full-duplex mode. In addition, it allows the - baudrate to be set between 300 and 4800 baud (however not all modems - support all baudrates). This is the preferred driver. The next - driver, "BAYCOM ser12 half-duplex driver for AX.25" is the old - driver and still provided in case this driver does not work with - your serial interface chip. To configure the driver, use the sethdlc - utility available in the standard ax25 utilities package. - For more information on the modems, see - <file:Documentation/networking/device_drivers/hamradio/baycom.rst>. - - To compile this driver as a module, choose M here: the module - will be called baycom_ser_fdx. This is recommended. - -config BAYCOM_SER_HDX - tristate "BAYCOM ser12 halfduplex driver for AX.25" - depends on AX25 && HAS_IOPORT - select CRC_CCITT - help - This is one of two drivers for Baycom style simple amateur radio - modems that connect to a serial interface. The driver supports the - ser12 design in half-duplex mode. This is the old driver. It is - still provided in case your serial interface chip does not work with - the full-duplex driver. This driver is deprecated. To configure - the driver, use the sethdlc utility available in the standard ax25 - utilities package. For more information on the modems, see - <file:Documentation/networking/device_drivers/hamradio/baycom.rst>. - - To compile this driver as a module, choose M here: the module - will be called baycom_ser_hdx. This is recommended. - -config BAYCOM_PAR - tristate "BAYCOM picpar and par96 driver for AX.25" - depends on PARPORT && AX25 - select CRC_CCITT - help - This is a driver for Baycom style simple amateur radio modems that - connect to a parallel interface. The driver supports the picpar and - par96 designs. To configure the driver, use the sethdlc utility - available in the standard ax25 utilities package. - For more information on the modems, see - <file:Documentation/networking/device_drivers/hamradio/baycom.rst>. - - To compile this driver as a module, choose M here: the module - will be called baycom_par. This is recommended. - -config BAYCOM_EPP - tristate "BAYCOM epp driver for AX.25" - depends on PARPORT && AX25 && !64BIT - select CRC_CCITT - help - This is a driver for Baycom style simple amateur radio modems that - connect to a parallel interface. The driver supports the EPP - designs. To configure the driver, use the sethdlc utility available - in the standard ax25 utilities package. - For more information on the modems, see - <file:Documentation/networking/device_drivers/hamradio/baycom.rst>. - - To compile this driver as a module, choose M here: the module - will be called baycom_epp. This is recommended. - -config YAM - tristate "YAM driver for AX.25" - depends on AX25 && HAS_IOPORT - help - The YAM is a modem for packet radio which connects to the serial - port and includes some of the functions of a Terminal Node - Controller. If you have one of those, say Y here. - - To compile this driver as a module, choose M here: the module - will be called yam. - - diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile deleted file mode 100644 index 25fc400369ba..000000000000 --- a/drivers/net/hamradio/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the Linux AX.25 and HFMODEM device drivers. -# -# -# 19971130 Moved the amateur radio related network drivers from -# drivers/net/ to drivers/hamradio for easier maintenance. -# Joerg Reuter DL1BKE <jreuter@yaina.de> -# -# 20000806 Rewritten to use lists instead of if-statements. -# Christoph Hellwig <hch@infradead.org> -# - -obj-$(CONFIG_SCC) += scc.o -obj-$(CONFIG_MKISS) += mkiss.o -obj-$(CONFIG_6PACK) += 6pack.o -obj-$(CONFIG_YAM) += yam.o -obj-$(CONFIG_BPQETHER) += bpqether.o -obj-$(CONFIG_BAYCOM_SER_FDX) += baycom_ser_fdx.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_SER_HDX) += baycom_ser_hdx.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_PAR) += baycom_par.o hdlcdrv.o -obj-$(CONFIG_BAYCOM_EPP) += baycom_epp.o hdlcdrv.o diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c deleted file mode 100644 index 5fda7a0fcce0..000000000000 --- a/drivers/net/hamradio/baycom_epp.c +++ /dev/null @@ -1,1316 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_epp.c -- baycom epp radio modem driver. - * - * Copyright (C) 1998-2000 - * Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * History: - * 0.1 xx.xx.1998 Initial version by Matthias Welwarsky (dg2fef) - * 0.2 21.04.1998 Massive rework by Thomas Sailer - * Integrated FPGA EPP modem configuration routines - * 0.3 11.05.1998 Took FPGA config out and moved it into a separate program - * 0.4 26.07.1999 Adapted to new lowlevel parport driver interface - * 0.5 03.08.1999 adapt to Linus' new __setup/__initcall - * removed some pre-2.2 kernel compatibility cruft - * 0.6 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts - * 0.7 12.02.2000 adapted to softnet driver interface - */ - -/*****************************************************************************/ - -#include <linux/crc-ccitt.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/workqueue.h> -#include <linux/fs.h> -#include <linux/parport.h> -#include <linux/if_arp.h> -#include <linux/hdlcdrv.h> -#include <linux/baycom.h> -#include <linux/jiffies.h> -#include <linux/random.h> -#include <net/ax25.h> -#include <linux/uaccess.h> - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG -#define BAYCOM_MAGIC 19730510 - -/* --------------------------------------------------------------------- */ - -static const char paranoia_str[] = KERN_ERR - "baycom_epp: bad magic number for hdlcdrv_state struct in routine %s\n"; - -static const char bc_drvname[] = "baycom_epp"; -static const char bc_drvinfo[] = KERN_INFO "baycom_epp: (C) 1998-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_epp: version 0.7\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -/* EPP status register */ -#define EPP_DCDBIT 0x80 -#define EPP_PTTBIT 0x08 -#define EPP_NREF 0x01 -#define EPP_NRAEF 0x02 -#define EPP_NRHF 0x04 -#define EPP_NTHF 0x20 -#define EPP_NTAEF 0x10 -#define EPP_NTEF EPP_PTTBIT - -/* EPP control register */ -#define EPP_TX_FIFO_ENABLE 0x10 -#define EPP_RX_FIFO_ENABLE 0x08 -#define EPP_MODEM_ENABLE 0x20 -#define EPP_LEDS 0xC0 -#define EPP_IRQ_ENABLE 0x10 - -/* LPT registers */ -#define LPTREG_ECONTROL 0x402 -#define LPTREG_CONFIGB 0x401 -#define LPTREG_CONFIGA 0x400 -#define LPTREG_EPPDATA 0x004 -#define LPTREG_EPPADDR 0x003 -#define LPTREG_CONTROL 0x002 -#define LPTREG_STATUS 0x001 -#define LPTREG_DATA 0x000 - -/* LPT control register */ -#define LPTCTRL_PROGRAM 0x04 /* 0 to reprogram */ -#define LPTCTRL_WRITE 0x01 -#define LPTCTRL_ADDRSTB 0x08 -#define LPTCTRL_DATASTB 0x02 -#define LPTCTRL_INTEN 0x10 - -/* LPT status register */ -#define LPTSTAT_SHIFT_NINTR 6 -#define LPTSTAT_WAIT 0x80 -#define LPTSTAT_NINTR (1<<LPTSTAT_SHIFT_NINTR) -#define LPTSTAT_PE 0x20 -#define LPTSTAT_DONE 0x10 -#define LPTSTAT_NERROR 0x08 -#define LPTSTAT_EPPTIMEOUT 0x01 - -/* LPT data register */ -#define LPTDATA_SHIFT_TDI 0 -#define LPTDATA_SHIFT_TMS 2 -#define LPTDATA_TDI (1<<LPTDATA_SHIFT_TDI) -#define LPTDATA_TCK 0x02 -#define LPTDATA_TMS (1<<LPTDATA_SHIFT_TMS) -#define LPTDATA_INITBIAS 0x80 - - -/* EPP modem config/status bits */ -#define EPP_DCDBIT 0x80 -#define EPP_PTTBIT 0x08 -#define EPP_RXEBIT 0x01 -#define EPP_RXAEBIT 0x02 -#define EPP_RXHFULL 0x04 - -#define EPP_NTHF 0x20 -#define EPP_NTAEF 0x10 -#define EPP_NTEF EPP_PTTBIT - -#define EPP_TX_FIFO_ENABLE 0x10 -#define EPP_RX_FIFO_ENABLE 0x08 -#define EPP_MODEM_ENABLE 0x20 -#define EPP_LEDS 0xC0 -#define EPP_IRQ_ENABLE 0x10 - -/* Xilinx 4k JTAG instructions */ -#define XC4K_IRLENGTH 3 -#define XC4K_EXTEST 0 -#define XC4K_PRELOAD 1 -#define XC4K_CONFIGURE 5 -#define XC4K_BYPASS 7 - -#define EPP_CONVENTIONAL 0 -#define EPP_FPGA 1 -#define EPP_FPGAEXTSTATUS 2 - -#define TXBUFFER_SIZE ((HDLCDRV_MAXFLEN*6/5)+8) - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - int magic; - - struct pardevice *pdev; - struct net_device *dev; - unsigned int work_running; - struct delayed_work run_work; - unsigned int modem; - unsigned int bitrate; - unsigned char stat; - - struct { - unsigned int intclk; - unsigned int fclk; - unsigned int bps; - unsigned int extmodem; - unsigned int loopback; - } cfg; - - struct hdlcdrv_channel_params ch_params; - - struct { - unsigned int bitbuf, bitstream, numbits, state; - unsigned char *bufptr; - int bufcnt; - unsigned char buf[TXBUFFER_SIZE]; - } hdlcrx; - - struct { - int calibrate; - int slotcnt; - int flags; - enum { tx_idle = 0, tx_keyup, tx_data, tx_tail } state; - unsigned char *bufptr; - int bufcnt; - unsigned char buf[TXBUFFER_SIZE]; - } hdlctx; - - unsigned int ptt_keyed; - struct sk_buff *skb; /* next transmit packet */ - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - unsigned int mod_cycles; - unsigned int demod_cycles; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -#define KISS_VERBOSE - -/* --------------------------------------------------------------------- */ - -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_TXTAIL 4 -#define PARAM_FULLDUP 5 -#define PARAM_HARDWARE 6 -#define PARAM_RETURN 255 - -/* --------------------------------------------------------------------- */ -/* - * the CRC routines are stolen from WAMPES - * by Dieter Deyke - */ - - -/*---------------------------------------------------------------------------*/ - -#if 0 -static inline void append_crc_ccitt(unsigned char *buffer, int len) -{ - unsigned int crc = 0xffff; - - for (;len>0;len--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buffer++) & 0xff]; - crc ^= 0xffff; - *buffer++ = crc; - *buffer++ = crc >> 8; -} -#endif - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int calc_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) ^ 0xffff) & 0xffff; -} - -/* ---------------------------------------------------------------------- */ - -#define tenms_to_flags(bc,tenms) ((tenms * bc->bitrate) / 800) - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* ---------------------------------------------------------------------- */ -/* - * eppconfig_path should be setable via /proc/sys. - */ - -static char const eppconfig_path[] = "/usr/sbin/eppfpga"; - -static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; - -/* eppconfig: called during ifconfig up to configure the modem */ -static int eppconfig(struct baycom_state *bc) -{ - char modearg[256]; - char portarg[16]; - char *argv[] = { - (char *)eppconfig_path, - "-s", - "-p", portarg, - "-m", modearg, - NULL }; - - /* set up arguments */ - sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, - (bc->cfg.fclk + 8 * bc->cfg.bps) / (16 * bc->cfg.bps), - bc->cfg.loopback ? ",loopback" : ""); - sprintf(portarg, "%ld", bc->pdev->port->base); - printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); - - return call_usermodehelper(eppconfig_path, argv, envp, UMH_WAIT_PROC); -} - -/* ---------------------------------------------------------------------- */ - -static inline void do_kiss_params(struct baycom_state *bc, - unsigned char *data, unsigned long len) -{ - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "baycomm_epp: channel params: " a "\n", b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - bc->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * bc->ch_params.tx_delay); - break; - case PARAM_PERSIST: - bc->ch_params.ppersist = data[1]; - PKP("p persistence = %u", bc->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - bc->ch_params.slottime = data[1]; - PKP("slot time = %ums", bc->ch_params.slottime); - break; - case PARAM_TXTAIL: - bc->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", bc->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - bc->ch_params.fulldup = !!data[1]; - PKP("%s duplex", bc->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* --------------------------------------------------------------------- */ - -static void encode_hdlc(struct baycom_state *bc) -{ - struct sk_buff *skb; - unsigned char *wp, *bp; - int pkt_len; - unsigned bitstream, notbitstream, bitbuf, numbit, crc; - unsigned char crcarr[2]; - int j; - - if (bc->hdlctx.bufcnt > 0) - return; - skb = bc->skb; - if (!skb) - return; - bc->skb = NULL; - pkt_len = skb->len-1; /* strip KISS byte */ - wp = bc->hdlctx.buf; - bp = skb->data+1; - crc = calc_crc_ccitt(bp, pkt_len); - crcarr[0] = crc; - crcarr[1] = crc >> 8; - *wp++ = 0x7e; - bitstream = bitbuf = numbit = 0; - while (pkt_len > -2) { - bitstream >>= 8; - bitstream |= ((unsigned int)*bp) << 8; - bitbuf |= ((unsigned int)*bp) << numbit; - notbitstream = ~bitstream; - bp++; - pkt_len--; - if (!pkt_len) - bp = crcarr; - for (j = 0; j < 8; j++) - if (unlikely(!(notbitstream & (0x1f0 << j)))) { - bitstream &= ~(0x100 << j); - bitbuf = (bitbuf & (((2 << j) << numbit) - 1)) | - ((bitbuf & ~(((2 << j) << numbit) - 1)) << 1); - numbit++; - notbitstream = ~bitstream; - } - numbit += 8; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - } - bitbuf |= 0x7e7e << numbit; - numbit += 16; - while (numbit >= 8) { - *wp++ = bitbuf; - bitbuf >>= 8; - numbit -= 8; - } - bc->hdlctx.bufptr = bc->hdlctx.buf; - bc->hdlctx.bufcnt = wp - bc->hdlctx.buf; - dev_kfree_skb(skb); - bc->dev->stats.tx_packets++; -} - -/* ---------------------------------------------------------------------- */ - -static int transmit(struct baycom_state *bc, int cnt, unsigned char stat) -{ - struct parport *pp = bc->pdev->port; - unsigned char tmp[128]; - int i, j; - - if (bc->hdlctx.state == tx_tail && !(stat & EPP_PTTBIT)) - bc->hdlctx.state = tx_idle; - if (bc->hdlctx.state == tx_idle && bc->hdlctx.calibrate <= 0) { - if (bc->hdlctx.bufcnt <= 0) - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) - return 0; - if (!bc->ch_params.fulldup) { - if (!(stat & EPP_DCDBIT)) { - bc->hdlctx.slotcnt = bc->ch_params.slottime; - return 0; - } - if ((--bc->hdlctx.slotcnt) > 0) - return 0; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - if (get_random_u8() > bc->ch_params.ppersist) - return 0; - } - } - if (bc->hdlctx.state == tx_idle && bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_keyup; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_delay); - bc->ptt_keyed++; - } - while (cnt > 0) { - switch (bc->hdlctx.state) { - case tx_keyup: - i = min_t(int, cnt, bc->hdlctx.flags); - cnt -= i; - bc->hdlctx.flags -= i; - if (bc->hdlctx.flags <= 0) - bc->hdlctx.state = tx_data; - memset(tmp, 0x7e, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - - case tx_data: - if (bc->hdlctx.bufcnt <= 0) { - encode_hdlc(bc); - if (bc->hdlctx.bufcnt <= 0) { - bc->hdlctx.state = tx_tail; - bc->hdlctx.flags = tenms_to_flags(bc, bc->ch_params.tx_tail); - break; - } - } - i = min_t(int, cnt, bc->hdlctx.bufcnt); - bc->hdlctx.bufcnt -= i; - cnt -= i; - if (i != pp->ops->epp_write_data(pp, bc->hdlctx.bufptr, i, 0)) - return -1; - bc->hdlctx.bufptr += i; - break; - - case tx_tail: - encode_hdlc(bc); - if (bc->hdlctx.bufcnt > 0) { - bc->hdlctx.state = tx_data; - break; - } - i = min_t(int, cnt, bc->hdlctx.flags); - if (i) { - cnt -= i; - bc->hdlctx.flags -= i; - memset(tmp, 0x7e, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - } - fallthrough; - - default: - if (bc->hdlctx.calibrate <= 0) - return 0; - i = min_t(int, cnt, bc->hdlctx.calibrate); - cnt -= i; - bc->hdlctx.calibrate -= i; - memset(tmp, 0, sizeof(tmp)); - while (i > 0) { - j = (i > sizeof(tmp)) ? sizeof(tmp) : i; - if (j != pp->ops->epp_write_data(pp, tmp, j, 0)) - return -1; - i -= j; - } - break; - } - } - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static void do_rxpacket(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct sk_buff *skb; - unsigned char *cp; - unsigned pktlen; - - if (bc->hdlcrx.bufcnt < 4) - return; - if (!check_crc_ccitt(bc->hdlcrx.buf, bc->hdlcrx.bufcnt)) - return; - pktlen = bc->hdlcrx.bufcnt-2+1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pktlen))) { - printk("%s: memory squeeze, dropping packet\n", dev->name); - dev->stats.rx_dropped++; - return; - } - cp = skb_put(skb, pktlen); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, bc->hdlcrx.buf, pktlen - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; -} - -static int receive(struct net_device *dev, int cnt) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = bc->pdev->port; - unsigned int bitbuf, notbitstream, bitstream, numbits, state; - unsigned char tmp[128]; - unsigned char *cp; - int cnt2, ret = 0; - int j; - - numbits = bc->hdlcrx.numbits; - state = bc->hdlcrx.state; - bitstream = bc->hdlcrx.bitstream; - bitbuf = bc->hdlcrx.bitbuf; - while (cnt > 0) { - cnt2 = (cnt > sizeof(tmp)) ? sizeof(tmp) : cnt; - cnt -= cnt2; - if (cnt2 != pp->ops->epp_read_data(pp, tmp, cnt2, 0)) { - ret = -1; - break; - } - cp = tmp; - for (; cnt2 > 0; cnt2--, cp++) { - bitstream >>= 8; - bitstream |= (*cp) << 8; - bitbuf >>= 8; - bitbuf |= (*cp) << 8; - numbits += 8; - notbitstream = ~bitstream; - for (j = 0; j < 8; j++) { - - /* flag or abort */ - if (unlikely(!(notbitstream & (0x0fc << j)))) { - - /* abort received */ - if (!(notbitstream & (0x1fc << j))) - state = 0; - - /* flag received */ - else if ((bitstream & (0x1fe << j)) == (0x0fc << j)) { - if (state) - do_rxpacket(dev); - bc->hdlcrx.bufcnt = 0; - bc->hdlcrx.bufptr = bc->hdlcrx.buf; - state = 1; - numbits = 7-j; - } - } - - /* stuffed bit */ - else if (unlikely((bitstream & (0x1f8 << j)) == (0xf8 << j))) { - numbits--; - bitbuf = (bitbuf & ((~0xff) << j)) | ((bitbuf & ~((~0xff) << j)) << 1); - } - } - while (state && numbits >= 8) { - if (bc->hdlcrx.bufcnt >= TXBUFFER_SIZE) { - state = 0; - } else { - *(bc->hdlcrx.bufptr)++ = bitbuf >> (16-numbits); - bc->hdlcrx.bufcnt++; - numbits -= 8; - } - } - } - } - bc->hdlcrx.numbits = numbits; - bc->hdlcrx.state = state; - bc->hdlcrx.bitstream = bitstream; - bc->hdlcrx.bitbuf = bitbuf; - return ret; -} - -/* --------------------------------------------------------------------- */ - -#define GETTICK(x) \ -({ \ - x = (unsigned int)get_cycles(); \ -}) - -static void epp_bh(struct work_struct *work) -{ - struct net_device *dev; - struct baycom_state *bc; - struct parport *pp; - unsigned char stat; - unsigned char tmp[2]; - unsigned int time1 = 0, time2 = 0, time3 = 0; - int cnt, cnt2; - - bc = container_of(work, struct baycom_state, run_work.work); - dev = bc->dev; - if (!bc->work_running) - return; - baycom_int_freq(bc); - pp = bc->pdev->port; - /* update status */ - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - bc->stat = stat; - bc->debug_vals.last_pllcorr = stat; - GETTICK(time1); - if (bc->modem == EPP_FPGAEXTSTATUS) { - /* get input count */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|1; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - cnt = tmp[0] | (tmp[1] << 8); - cnt &= 0x7fff; - /* get output count */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE|2; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - cnt2 = tmp[0] | (tmp[1] << 8); - cnt2 = 16384 - (cnt2 & 0x7fff); - /* return to normal */ - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - if (transmit(bc, cnt2, stat)) - goto epptimeout; - GETTICK(time2); - if (receive(dev, cnt)) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - bc->stat = stat; - } else { - /* try to tx */ - switch (stat & (EPP_NTAEF|EPP_NTHF)) { - case EPP_NTHF: - cnt = 2048 - 256; - break; - - case EPP_NTAEF: - cnt = 2048 - 1793; - break; - - case 0: - cnt = 0; - break; - - default: - cnt = 2048 - 1025; - break; - } - if (transmit(bc, cnt, stat)) - goto epptimeout; - GETTICK(time2); - /* do receiver */ - while ((stat & (EPP_NRAEF|EPP_NRHF)) != EPP_NRHF) { - switch (stat & (EPP_NRAEF|EPP_NRHF)) { - case EPP_NRAEF: - cnt = 1025; - break; - - case 0: - cnt = 1793; - break; - - default: - cnt = 256; - break; - } - if (receive(dev, cnt)) - goto epptimeout; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - } - cnt = 0; - if (bc->bitrate < 50000) - cnt = 256; - else if (bc->bitrate < 100000) - cnt = 128; - while (cnt > 0 && stat & EPP_NREF) { - if (receive(dev, 1)) - goto epptimeout; - cnt--; - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - } - } - GETTICK(time3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.mod_cycles = time2 - time1; - bc->debug_vals.demod_cycles = time3 - time2; -#endif /* BAYCOM_DEBUG */ - schedule_delayed_work(&bc->run_work, 1); - if (!bc->skb) - netif_wake_queue(dev); - return; - epptimeout: - printk(KERN_ERR "%s: EPP timeout!\n", bc_drvname); -} - -/* ---------------------------------------------------------------------- */ -/* - * ===================== network driver interface ========================= - */ - -static netdev_tx_t baycom_send_packet(struct sk_buff *skb, struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->data[0] != 0) { - do_kiss_params(bc, skb->data, skb->len); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - if (bc->skb) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - /* strip KISS byte */ - if (skb->len >= HDLCDRV_MAXFLEN+1 || skb->len < 3) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - netif_stop_queue(dev); - bc->skb = skb; - return NETDEV_TX_OK; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void epp_wakeup(void *handle) -{ - struct net_device *dev = (struct net_device *)handle; - struct baycom_state *bc = netdev_priv(dev); - - printk(KERN_DEBUG "baycom_epp: %s: why am I being woken up?\n", dev->name); - if (!parport_claim(bc->pdev)) - printk(KERN_DEBUG "baycom_epp: %s: I'm broken.\n", dev->name); -} - -/* --------------------------------------------------------------------- */ - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int epp_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = parport_find_base(dev->base_addr); - unsigned int i, j; - unsigned char tmp[128]; - unsigned char stat; - unsigned long tstart; - struct pardev_cb par_cb; - - if (!pp) { - printk(KERN_ERR "%s: parport at 0x%lx unknown\n", bc_drvname, dev->base_addr); - return -ENXIO; - } -#if 0 - if (pp->irq < 0) { - printk(KERN_ERR "%s: parport at 0x%lx has no irq\n", bc_drvname, pp->base); - parport_put_port(pp); - return -ENXIO; - } -#endif - if ((~pp->modes) & (PARPORT_MODE_TRISTATE | PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) { - printk(KERN_ERR "%s: parport at 0x%lx cannot be used\n", - bc_drvname, pp->base); - parport_put_port(pp); - return -EIO; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - memset(&par_cb, 0, sizeof(par_cb)); - par_cb.wakeup = epp_wakeup; - par_cb.private = (void *)dev; - par_cb.flags = PARPORT_DEV_EXCL; - for (i = 0; i < NR_PORTS; i++) - if (baycom_device[i] == dev) - break; - - if (i == NR_PORTS) { - pr_err("%s: no device found\n", bc_drvname); - parport_put_port(pp); - return -ENODEV; - } - - bc->pdev = parport_register_dev_model(pp, dev->name, &par_cb, i); - parport_put_port(pp); - if (!bc->pdev) { - printk(KERN_ERR "%s: cannot register parport at 0x%lx\n", bc_drvname, pp->base); - return -ENXIO; - } - if (parport_claim(bc->pdev)) { - printk(KERN_ERR "%s: parport at 0x%lx busy\n", bc_drvname, pp->base); - parport_unregister_device(bc->pdev); - return -EBUSY; - } - dev->irq = /*pp->irq*/ 0; - INIT_DELAYED_WORK(&bc->run_work, epp_bh); - bc->work_running = 1; - bc->modem = EPP_CONVENTIONAL; - if (eppconfig(bc)) - printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname); - else - bc->modem = /*EPP_FPGA*/ EPP_FPGAEXTSTATUS; - parport_write_control(pp, LPTCTRL_PROGRAM); /* prepare EPP mode; we aren't using interrupts */ - /* reset the modem */ - tmp[0] = 0; - tmp[1] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE; - if (pp->ops->epp_write_addr(pp, tmp, 2, 0) != 2) - goto epptimeout; - /* autoprobe baud rate */ - tstart = jiffies; - i = 0; - while (time_before(jiffies, tstart + HZ/3)) { - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - if ((stat & (EPP_NRAEF|EPP_NRHF)) == EPP_NRHF) { - schedule(); - continue; - } - if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) - goto epptimeout; - if (pp->ops->epp_read_data(pp, tmp, 128, 0) != 128) - goto epptimeout; - i += 256; - } - for (j = 0; j < 256; j++) { - if (pp->ops->epp_read_addr(pp, &stat, 1, 0) != 1) - goto epptimeout; - if (!(stat & EPP_NREF)) - break; - if (pp->ops->epp_read_data(pp, tmp, 1, 0) != 1) - goto epptimeout; - i++; - } - tstart = jiffies - tstart; - bc->bitrate = i * (8 * HZ) / tstart; - j = 1; - i = bc->bitrate >> 3; - while (j < 7 && i > 150) { - j++; - i >>= 1; - } - printk(KERN_INFO "%s: autoprobed bitrate: %d int divider: %d int rate: %d\n", - bc_drvname, bc->bitrate, j, bc->bitrate >> (j+2)); - tmp[0] = EPP_TX_FIFO_ENABLE|EPP_RX_FIFO_ENABLE|EPP_MODEM_ENABLE/*|j*/; - if (pp->ops->epp_write_addr(pp, tmp, 1, 0) != 1) - goto epptimeout; - /* - * initialise hdlc variables - */ - bc->hdlcrx.state = 0; - bc->hdlcrx.numbits = 0; - bc->hdlctx.state = tx_idle; - bc->hdlctx.bufcnt = 0; - bc->hdlctx.slotcnt = bc->ch_params.slottime; - bc->hdlctx.calibrate = 0; - /* start the bottom half stuff */ - schedule_delayed_work(&bc->run_work, 1); - netif_start_queue(dev); - return 0; - - epptimeout: - printk(KERN_ERR "%s: epp timeout during bitrate probe\n", bc_drvname); - parport_write_control(pp, 0); /* reset the adapter */ - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - return -EIO; -} - -/* --------------------------------------------------------------------- */ - -static int epp_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp = bc->pdev->port; - unsigned char tmp[1]; - - bc->work_running = 0; - cancel_delayed_work_sync(&bc->run_work); - bc->stat = EPP_DCDBIT; - tmp[0] = 0; - pp->ops->epp_write_addr(pp, tmp, 1, 0); - parport_write_control(pp, 0); /* reset the adapter */ - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - dev_kfree_skb(bc->skb); - bc->skb = NULL; - printk(KERN_INFO "%s: close epp at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - const char *cp; - - if (strstr(modestr,"intclk")) - bc->cfg.intclk = 1; - if (strstr(modestr,"extclk")) - bc->cfg.intclk = 0; - if (strstr(modestr,"intmodem")) - bc->cfg.extmodem = 0; - if (strstr(modestr,"extmodem")) - bc->cfg.extmodem = 1; - if (strstr(modestr,"loopback")) - bc->cfg.loopback = 1; - if (strstr(modestr, "noloopback")) - bc->cfg.loopback = 0; - if ((cp = strstr(modestr,"fclk="))) { - bc->cfg.fclk = simple_strtoul(cp+5, NULL, 0); - if (bc->cfg.fclk < 1000000) - bc->cfg.fclk = 1000000; - if (bc->cfg.fclk > 25000000) - bc->cfg.fclk = 25000000; - } - if ((cp = strstr(modestr,"bps="))) { - bc->cfg.bps = simple_strtoul(cp+4, NULL, 0); - if (bc->cfg.bps < 1000) - bc->cfg.bps = 1000; - if (bc->cfg.bps > 1500000) - bc->cfg.bps = 1500000; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct baycom_state *bc = netdev_priv(dev); - struct hdlcdrv_ioctl hi; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (copy_from_user(&hi, data, sizeof(hi))) - return -EFAULT; - switch (hi.cmd) { - default: - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETCHANNELPAR: - hi.data.cp.tx_delay = bc->ch_params.tx_delay; - hi.data.cp.tx_tail = bc->ch_params.tx_tail; - hi.data.cp.slottime = bc->ch_params.slottime; - hi.data.cp.ppersist = bc->ch_params.ppersist; - hi.data.cp.fulldup = bc->ch_params.fulldup; - break; - - case HDLCDRVCTL_SETCHANNELPAR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - bc->ch_params.tx_delay = hi.data.cp.tx_delay; - bc->ch_params.tx_tail = hi.data.cp.tx_tail; - bc->ch_params.slottime = hi.data.cp.slottime; - bc->ch_params.ppersist = hi.data.cp.ppersist; - bc->ch_params.fulldup = hi.data.cp.fulldup; - bc->hdlctx.slotcnt = 1; - return 0; - - case HDLCDRVCTL_GETMODEMPAR: - hi.data.mp.iobase = dev->base_addr; - hi.data.mp.irq = dev->irq; - hi.data.mp.dma = dev->dma; - hi.data.mp.dma2 = 0; - hi.data.mp.seriobase = 0; - hi.data.mp.pariobase = 0; - hi.data.mp.midiiobase = 0; - break; - - case HDLCDRVCTL_SETMODEMPAR: - if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) - return -EACCES; - dev->base_addr = hi.data.mp.iobase; - dev->irq = /*hi.data.mp.irq*/0; - dev->dma = /*hi.data.mp.dma*/0; - return 0; - - case HDLCDRVCTL_GETSTAT: - hi.data.cs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.cs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.cs.ptt_keyed = bc->ptt_keyed; - hi.data.cs.tx_packets = dev->stats.tx_packets; - hi.data.cs.tx_errors = dev->stats.tx_errors; - hi.data.cs.rx_packets = dev->stats.rx_packets; - hi.data.cs.rx_errors = dev->stats.rx_errors; - break; - - case HDLCDRVCTL_OLDGETSTAT: - hi.data.ocs.ptt = !!(bc->stat & EPP_PTTBIT); - hi.data.ocs.dcd = !(bc->stat & EPP_DCDBIT); - hi.data.ocs.ptt_keyed = bc->ptt_keyed; - break; - - case HDLCDRVCTL_CALIBRATE: - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - bc->hdlctx.calibrate = hi.data.calibrate * bc->bitrate / 8; - return 0; - - case HDLCDRVCTL_DRIVERNAME: - strscpy_pad(hi.data.drivername, "baycom_epp"); - break; - - case HDLCDRVCTL_GETMODE: - sprintf(hi.data.modename, "%sclk,%smodem,fclk=%d,bps=%d%s", - bc->cfg.intclk ? "int" : "ext", - bc->cfg.extmodem ? "ext" : "int", bc->cfg.fclk, bc->cfg.bps, - bc->cfg.loopback ? ",loopback" : ""); - break; - - case HDLCDRVCTL_SETMODE: - if (!capable(CAP_NET_ADMIN) || netif_running(dev)) - return -EACCES; - hi.data.modename[sizeof(hi.data.modename)-1] = '\0'; - return baycom_setmode(bc, hi.data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy_pad(hi.data.modename, "intclk,extclk,intmodem,extmodem,divider=x"); - break; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE; - - } - if (copy_to_user(data, &hi, sizeof(hi))) - return -EFAULT; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops baycom_netdev_ops = { - .ndo_open = epp_open, - .ndo_stop = epp_close, - .ndo_siocdevprivate = baycom_siocdevprivate, - .ndo_start_xmit = baycom_send_packet, - .ndo_set_mac_address = baycom_set_mac_address, -}; - -/* - * Check for a network adaptor of this type, and return '0' if one exists. - * If dev->base_addr == 0, probe all likely locations. - * If dev->base_addr == 1, always return failure. - * If dev->base_addr == 2, allocate space for the device and return success - * (detachable devices only). - */ -static void baycom_probe(struct net_device *dev) -{ - const struct hdlcdrv_channel_params dflt_ch_params = { - 20, 2, 10, 40, 0 - }; - struct baycom_state *bc; - - /* - * not a real probe! only initialize data structures - */ - bc = netdev_priv(dev); - /* - * initialize the baycom_state struct - */ - bc->ch_params = dflt_ch_params; - bc->ptt_keyed = 0; - - /* - * initialize the device struct - */ - - /* Fill in the fields of the device structure */ - bc->skb = NULL; - - dev->netdev_ops = &baycom_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ - dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&null_ax25_address); - dev->tx_queue_len = 16; - - /* New style flags */ - dev->flags = 0; -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "", }; -static int iobase[NR_PORTS] = { 0x378, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom epp amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int baycom_epp_par_probe(struct pardevice *par_dev) -{ - struct device_driver *drv = par_dev->dev.driver; - int len = strlen(drv->name); - - if (strncmp(par_dev->name, drv->name, len)) - return -ENODEV; - - return 0; -} - -static struct parport_driver baycom_epp_par_driver = { - .name = "bce", - .probe = baycom_epp_par_probe, -}; - -static void __init baycom_epp_dev_setup(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - /* - * initialize part of the baycom_state struct - */ - bc->dev = dev; - bc->magic = BAYCOM_MAGIC; - bc->cfg.fclk = 19666600; - bc->cfg.bps = 9600; - /* - * initialize part of the device struct - */ - baycom_probe(dev); -} - -static int __init init_baycomepp(void) -{ - int i, found = 0, ret; - char set_hw = 1; - - printk(bc_drvinfo); - - ret = parport_register_driver(&baycom_epp_par_driver); - if (ret) - return ret; - - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - - dev = alloc_netdev(sizeof(struct baycom_state), "bce%d", - NET_NAME_UNKNOWN, baycom_epp_dev_setup); - - if (!dev) { - printk(KERN_WARNING "bce%d : out of memory\n", i); - return found ? 0 : -ENOMEM; - } - - sprintf(dev->name, "bce%d", i); - dev->base_addr = iobase[i]; - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = 0; - - if (register_netdev(dev)) { - printk(KERN_WARNING "%s: cannot register net device %s\n", bc_drvname, dev->name); - free_netdev(dev); - break; - } - if (set_hw && baycom_setmode(netdev_priv(dev), mode[i])) - set_hw = 0; - baycom_device[i] = dev; - found++; - } - - if (found == 0) { - parport_unregister_driver(&baycom_epp_par_driver); - return -ENXIO; - } - - return 0; -} - -static void __exit cleanup_baycomepp(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) { - struct baycom_state *bc = netdev_priv(dev); - if (bc->magic == BAYCOM_MAGIC) { - unregister_netdev(dev); - free_netdev(dev); - } else - printk(paranoia_str, "cleanup_module"); - } - } - parport_unregister_driver(&baycom_epp_par_driver); -} - -module_init(init_baycomepp); -module_exit(cleanup_baycomepp); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_epp=io,mode - * mode: fpga config options - */ - -static int __init baycom_epp_setup(char *str) -{ - static unsigned __initdata nr_dev = 0; - int ints[2]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 2, ints); - if (ints[0] < 1) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - nr_dev++; - return 1; -} - -__setup("baycom_epp=", baycom_epp_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_par.c b/drivers/net/hamradio/baycom_par.c deleted file mode 100644 index f03797103c6a..000000000000 --- a/drivers/net/hamradio/baycom_par.c +++ /dev/null @@ -1,598 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_par.c -- baycom par96 and picpar radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * par96: This is a modem for 9600 baud FSK compatible to the G3RUH standard. - * The modem does all the filtering and regenerates the receiver clock. - * Data is transferred from and to the PC via a shift register. - * The shift register is filled with 16 bits and an interrupt is - * signalled. The PC then empties the shift register in a burst. This - * modem connects to the parallel port, hence the name. The modem - * leaves the implementation of the HDLC protocol and the scrambler - * polynomial to the PC. This modem is no longer available (at least - * from Baycom) and has been replaced by the PICPAR modem (see below). - * You may however still build one from the schematics published in - * cq-DL :-). - * - * picpar: This is a redesign of the par96 modem by Henning Rech, DF9IC. The - * modem is protocol compatible to par96, but uses only three low - * power ICs and can therefore be fed from the parallel port and - * does not require an additional power supply. It features - * built in DCD circuitry. The driver should therefore be configured - * for hardware DCD. - * - * Command line options (insmod command line) - * - * mode driver mode string. Valid choices are par96 and picpar. - * iobase base address of the port; common values are 0x378, 0x278, 0x3bc - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 split into separate files for ser12/par96 - * 0.6 03.08.1999 adapt to Linus' new __setup/__initcall - * removed some pre-2.2 kernel compatibility cruft - * 0.7 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts - * 0.8 12.02.2000 adapted to softnet driver interface - * removed direct parport access, uses parport driver methods - * 0.9 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/fcntl.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/in.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/netdevice.h> -#include <linux/hdlcdrv.h> -#include <linux/baycom.h> -#include <linux/parport.h> -#include <linux/bitops.h> -#include <linux/jiffies.h> - -#include <linux/uaccess.h> - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* - * modem options; bit mask - */ -#define BAYCOM_OPTIONS_SOFTDCD 1 - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_par"; -static const char bc_drvinfo[] = KERN_INFO "baycom_par: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_par: version 0.9\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define PAR96_BURSTBITS 16 -#define PAR96_BURST 4 -#define PAR96_PTT 2 -#define PAR96_TXBIT 1 -#define PAR96_ACK 0x40 -#define PAR96_RXBIT 0x20 -#define PAR96_DCD 0x10 -#define PAR97_POWER 0xf8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - struct pardevice *pdev; - unsigned int options; - - struct modem_state { - short arb_divider; - unsigned char flags; - unsigned int shreg; - struct modem_state_par96 { - int dcd_count; - unsigned int dcd_shreg; - unsigned long descram; - unsigned long scram; - } par96; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== PAR96 specific routines ========================= - */ - -#define PAR96_DESCRAM_TAP1 0x20000 -#define PAR96_DESCRAM_TAP2 0x01000 -#define PAR96_DESCRAM_TAP3 0x00001 - -#define PAR96_DESCRAM_TAPSH1 17 -#define PAR96_DESCRAM_TAPSH2 12 -#define PAR96_DESCRAM_TAPSH3 0 - -#define PAR96_SCRAM_TAP1 0x20000 /* X^17 */ -#define PAR96_SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static inline void par96_tx(struct net_device *dev, struct baycom_state *bc) -{ - int i; - unsigned int data = hdlcdrv_getbits(&bc->hdrv); - struct parport *pp = bc->pdev->port; - - for(i = 0; i < PAR96_BURSTBITS; i++, data >>= 1) { - unsigned char val = PAR97_POWER; - bc->modem.par96.scram = ((bc->modem.par96.scram << 1) | - (bc->modem.par96.scram & 1)); - if (!(data & 1)) - bc->modem.par96.scram ^= 1; - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 1)) - bc->modem.par96.scram ^= - (PAR96_SCRAM_TAPN << 1); - if (bc->modem.par96.scram & (PAR96_SCRAM_TAP1 << 2)) - val |= PAR96_TXBIT; - pp->ops->write_data(pp, val); - pp->ops->write_data(pp, val | PAR96_BURST); - } -} - -/* --------------------------------------------------------------------- */ - -static inline void par96_rx(struct net_device *dev, struct baycom_state *bc) -{ - int i; - unsigned int data, mask, mask2, descx; - struct parport *pp = bc->pdev->port; - - /* - * do receiver; differential decode and descramble on the fly - */ - for(data = i = 0; i < PAR96_BURSTBITS; i++) { - bc->modem.par96.descram = (bc->modem.par96.descram << 1); - if (pp->ops->read_status(pp) & PAR96_RXBIT) - bc->modem.par96.descram |= 1; - descx = bc->modem.par96.descram ^ - (bc->modem.par96.descram >> 1); - /* now the diff decoded data is inverted in descram */ - pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT); - descx ^= ((descx >> PAR96_DESCRAM_TAPSH1) ^ - (descx >> PAR96_DESCRAM_TAPSH2)); - data >>= 1; - if (!(descx & 1)) - data |= 0x8000; - pp->ops->write_data(pp, PAR97_POWER | PAR96_PTT | PAR96_BURST); - } - hdlcdrv_putbits(&bc->hdrv, data); - /* - * do DCD algorithm - */ - if (bc->options & BAYCOM_OPTIONS_SOFTDCD) { - bc->modem.par96.dcd_shreg = (bc->modem.par96.dcd_shreg >> 16) - | (data << 16); - /* search for flags and set the dcd counter appropriately */ - for(mask = 0x1fe00, mask2 = 0xfc00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if ((bc->modem.par96.dcd_shreg & mask) == mask2) - bc->modem.par96.dcd_count = HDLCDRV_MAXFLEN+4; - /* check for abort/noise sequences */ - for(mask = 0x1fe00, mask2 = 0x1fe00, i = 0; - i < PAR96_BURSTBITS; i++, mask <<= 1, mask2 <<= 1) - if (((bc->modem.par96.dcd_shreg & mask) == mask2) && - (bc->modem.par96.dcd_count >= 0)) - bc->modem.par96.dcd_count -= HDLCDRV_MAXFLEN-10; - /* decrement and set the dcd variable */ - if (bc->modem.par96.dcd_count >= 0) - bc->modem.par96.dcd_count -= 2; - hdlcdrv_setdcd(&bc->hdrv, bc->modem.par96.dcd_count > 0); - } else { - hdlcdrv_setdcd(&bc->hdrv, !!(pp->ops->read_status(pp) & PAR96_DCD)); - } -} - -/* --------------------------------------------------------------------- */ - -static void par96_interrupt(void *dev_id) -{ - struct net_device *dev = dev_id; - struct baycom_state *bc = netdev_priv(dev); - - baycom_int_freq(bc); - /* - * check if transmitter active - */ - if (hdlcdrv_ptt(&bc->hdrv)) - par96_tx(dev, bc); - else { - par96_rx(dev, bc); - if (--bc->modem.arb_divider <= 0) { - bc->modem.arb_divider = 6; - local_irq_enable(); - hdlcdrv_arbitrate(dev, &bc->hdrv); - } - } - local_irq_enable(); - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); -} - -/* --------------------------------------------------------------------- */ - -static void par96_wakeup(void *handle) -{ - struct net_device *dev = (struct net_device *)handle; - struct baycom_state *bc = netdev_priv(dev); - - printk(KERN_DEBUG "baycom_par: %s: why am I being woken up?\n", dev->name); - if (!parport_claim(bc->pdev)) - printk(KERN_DEBUG "baycom_par: %s: I'm broken.\n", dev->name); -} - -/* --------------------------------------------------------------------- */ - -static int par96_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct pardev_cb par_cb; - struct parport *pp; - int i; - - if (!dev || !bc) - return -ENXIO; - pp = parport_find_base(dev->base_addr); - if (!pp) { - printk(KERN_ERR "baycom_par: parport at 0x%lx unknown\n", dev->base_addr); - return -ENXIO; - } - if (pp->irq < 0) { - printk(KERN_ERR "baycom_par: parport at 0x%lx has no irq\n", pp->base); - parport_put_port(pp); - return -ENXIO; - } - if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) { - printk(KERN_ERR "baycom_par: parport at 0x%lx cannot be used\n", pp->base); - parport_put_port(pp); - return -ENXIO; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = 9600; - memset(&par_cb, 0, sizeof(par_cb)); - par_cb.wakeup = par96_wakeup; - par_cb.irq_func = par96_interrupt; - par_cb.private = (void *)dev; - par_cb.flags = PARPORT_DEV_EXCL; - for (i = 0; i < NR_PORTS; i++) - if (baycom_device[i] == dev) - break; - - if (i == NR_PORTS) { - pr_err("%s: no device found\n", bc_drvname); - parport_put_port(pp); - return -ENODEV; - } - bc->pdev = parport_register_dev_model(pp, dev->name, &par_cb, i); - parport_put_port(pp); - if (!bc->pdev) { - printk(KERN_ERR "baycom_par: cannot register parport at 0x%lx\n", dev->base_addr); - return -ENXIO; - } - if (parport_claim(bc->pdev)) { - printk(KERN_ERR "baycom_par: parport at 0x%lx busy\n", pp->base); - parport_unregister_device(bc->pdev); - return -EBUSY; - } - pp = bc->pdev->port; - dev->irq = pp->irq; - pp->ops->data_forward(pp); - bc->hdrv.par.bitrate = 9600; - pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); /* switch off PTT */ - pp->ops->enable_irq(pp); - printk(KERN_INFO "%s: par96 at iobase 0x%lx irq %u options 0x%x\n", - bc_drvname, dev->base_addr, dev->irq, bc->options); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int par96_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - struct parport *pp; - - if (!dev || !bc) - return -EINVAL; - pp = bc->pdev->port; - /* disable interrupt */ - pp->ops->disable_irq(pp); - /* switch off PTT */ - pp->ops->write_data(pp, PAR96_PTT | PAR97_POWER); - parport_release(bc->pdev); - parport_unregister_device(bc->pdev); - printk(KERN_INFO "%s: close par96 at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops par96_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = par96_open, - .close = par96_close, - .ioctl = baycom_ioctl -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - if (!strncmp(modestr, "picpar", 6)) - bc->options = 0; - else if (!strncmp(modestr, "par96", 5)) - bc->options = BAYCOM_OPTIONS_SOFTDCD; - else - bc->options = !!strchr(modestr, '*'); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - strscpy(hi->data.modename, bc->options ? "par96" : "picpar"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "par96,picpar"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "picpar", }; -static int iobase[NR_PORTS] = { 0x378, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; eg. par96 or picpar"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int baycom_par_probe(struct pardevice *par_dev) -{ - struct device_driver *drv = par_dev->dev.driver; - int len = strlen(drv->name); - - if (strncmp(par_dev->name, drv->name, len)) - return -ENODEV; - - return 0; -} - -static struct parport_driver baycom_par_driver = { - .name = "bcp", - .probe = baycom_par_probe, -}; - -static int __init init_baycompar(void) -{ - int i, found = 0, ret; - char set_hw = 1; - - printk(bc_drvinfo); - - ret = parport_register_driver(&baycom_par_driver); - if (ret) - return ret; - - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcp%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = 0; - - dev = hdlcdrv_register(&par96_ops, - sizeof(struct baycom_state), - ifname, iobase[i], 0, 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - found++; - baycom_device[i] = dev; - } - - if (!found) { - parport_unregister_driver(&baycom_par_driver); - return -ENXIO; - } - return 0; -} - -static void __exit cleanup_baycompar(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) - hdlcdrv_unregister(dev); - } - parport_unregister_driver(&baycom_par_driver); -} - -module_init(init_baycompar); -module_exit(cleanup_baycompar); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_par=io,mode - * mode: par96,picpar - */ - -static int __init baycom_par_setup(char *str) -{ - static unsigned nr_dev; - int ints[2]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 2, ints); - if (ints[0] < 1) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - nr_dev++; - return 1; -} - -__setup("baycom_par=", baycom_par_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c deleted file mode 100644 index ee5bd3c12040..000000000000 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ /dev/null @@ -1,678 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_ser_fdx.c -- baycom ser12 fullduplex radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550A) - * - * This modem usually draws its supply current out of the otherwise unused - * TXD pin of the serial port. Thus a contiguous stream of 0x00-bytes - * is transmitted to achieve a positive supply voltage. - * - * hsk: This is a 4800 baud FSK modem, designed for TNC use. It works fine - * in 'baycom-mode' :-) In contrast to the TCM3105 modem, power is - * externally supplied. So there's no need to provide the 0x00-byte-stream - * when receiving or idle, which drastically reduces interrupt load. - * - * Command line options (insmod command line) - * - * mode ser# hardware DCD - * ser#* software DCD - * ser#+ hardware DCD, inverted signal at DCD pin - * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD' - * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 - * baud baud rate (between 300 and 4800) - * irq interrupt line of the port; common values are 4,3 - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 ser12/par96 split into separate files - * 0.6 24.01.1998 Thorsten Kranzkowski, dl8bcu and Thomas Sailer: - * reduced interrupt load in transmit case - * reworked receiver - * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall - * 0.8 10.08.1999 use module_init/module_exit - * 0.9 12.02.2000 adapted to softnet driver interface - * 0.10 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include <linux/capability.h> -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/hdlcdrv.h> -#include <linux/baycom.h> -#include <linux/jiffies.h> -#include <linux/time64.h> - -#include <linux/uaccess.h> -#include <asm/io.h> -#include <asm/irq.h> - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_ser_fdx"; -static const char bc_drvinfo[] = KERN_INFO "baycom_ser_fdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_fdx: version 0.10\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - unsigned int baud, baud_us, baud_arbdiv, baud_uartdiv, baud_dcdtimeout; - int opt_dcd; - - struct modem_state { - unsigned char flags; - unsigned char ptt; - unsigned int shreg; - struct modem_state_ser12 { - unsigned char tx_bit; - unsigned char last_rxbit; - int dcd_sum0, dcd_sum1, dcd_sum2; - int dcd_time; - unsigned int pll_time; - unsigned int txshreg; - } ser12; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -/* --------------------------------------------------------------------- */ - -static inline void ser12_set_divisor(struct net_device *dev, - unsigned int divisor) -{ - outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ - outb(divisor, DLL(dev->base_addr)); - outb(divisor >> 8, DLM(dev->base_addr)); - outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - /* - * it is important not to set the divider while transmitting; - * this reportedly makes some UARTs generating interrupts - * in the hundredthousands per second region - * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) - */ -} - -static __inline__ void ser12_rx(struct net_device *dev, struct baycom_state *bc, struct timespec64 *ts, unsigned char curs) -{ - int timediff; - int bdus8 = bc->baud_us >> 3; - int bdus4 = bc->baud_us >> 2; - int bdus2 = bc->baud_us >> 1; - - timediff = 1000000 + ts->tv_nsec / NSEC_PER_USEC - - bc->modem.ser12.pll_time; - while (timediff >= 500000) - timediff -= 1000000; - while (timediff >= bdus2) { - timediff -= bc->baud_us; - bc->modem.ser12.pll_time += bc->baud_us; - bc->modem.ser12.dcd_time--; - /* first check if there is room to add a bit */ - if (bc->modem.shreg & 1) { - hdlcdrv_putbits(&bc->hdrv, (bc->modem.shreg >> 1) ^ 0xffff); - bc->modem.shreg = 0x10000; - } - /* add a one bit */ - bc->modem.shreg >>= 1; - } - if (bc->modem.ser12.dcd_time <= 0) { - if (!bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - bc->modem.ser12.dcd_sum0 = 2; /* slight bias */ - bc->modem.ser12.dcd_time += 120; - } - if (bc->modem.ser12.last_rxbit != curs) { - bc->modem.ser12.last_rxbit = curs; - bc->modem.shreg |= 0x10000; - /* adjust the PLL */ - if (timediff > 0) - bc->modem.ser12.pll_time += bdus8; - else - bc->modem.ser12.pll_time += 1000000 - bdus8; - /* update DCD */ - if (abs(timediff) > bdus4) - bc->modem.ser12.dcd_sum0 += 4; - else - bc->modem.ser12.dcd_sum0--; -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr = timediff; -#endif /* BAYCOM_DEBUG */ - } - while (bc->modem.ser12.pll_time >= 1000000) - bc->modem.ser12.pll_time -= 1000000; -} - -/* --------------------------------------------------------------------- */ - -static irqreturn_t ser12_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct baycom_state *bc = netdev_priv(dev); - struct timespec64 ts; - unsigned char iir, msr; - unsigned int txcount = 0; - - if (!bc || bc->hdrv.magic != HDLCDRV_MAGIC) - return IRQ_NONE; - /* fast way out for shared irq */ - if ((iir = inb(IIR(dev->base_addr))) & 1) - return IRQ_NONE; - /* get current time */ - ktime_get_ts64(&ts); - msr = inb(MSR(dev->base_addr)); - /* delta DCD */ - if ((msr & 8) && bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80)); - do { - switch (iir & 6) { - case 6: - inb(LSR(dev->base_addr)); - break; - - case 4: - inb(RBR(dev->base_addr)); - break; - - case 2: - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - baycom_int_freq(bc); - txcount++; - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - if (bc->modem.ptt) - outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); - else - outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - break; - - default: - msr = inb(MSR(dev->base_addr)); - /* delta DCD */ - if ((msr & 8) && bc->opt_dcd) - hdlcdrv_setdcd(&bc->hdrv, !((msr ^ bc->opt_dcd) & 0x80)); - break; - } - iir = inb(IIR(dev->base_addr)); - } while (!(iir & 1)); - ser12_rx(dev, bc, &ts, msr & 0x10); /* CTS */ - if (bc->modem.ptt && txcount) { - if (bc->modem.ser12.txshreg <= 1) { - bc->modem.ser12.txshreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); - if (!hdlcdrv_ptt(&bc->hdrv)) { - ser12_set_divisor(dev, 115200/100/8); - bc->modem.ptt = 0; - goto end_transmit; - } - } - bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ (bc->modem.ser12.txshreg & 1)); - bc->modem.ser12.txshreg >>= 1; - } - end_transmit: - local_irq_enable(); - if (!bc->modem.ptt && txcount) { - hdlcdrv_arbitrate(dev, &bc->hdrv); - if (hdlcdrv_ptt(&bc->hdrv)) { - ser12_set_divisor(dev, bc->baud_uartdiv); - bc->modem.ser12.txshreg = 1; - bc->modem.ptt = 1; - } - } - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = { - "unknown", "8250", "16450", "16550", "16550A" -}; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_open(struct net_device *dev) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - struct baycom_state *bc = netdev_priv(dev); - enum uart u; - - if (!dev || !bc) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT || - dev->irq < 2 || dev->irq > nr_irqs) { - printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) " - "or irq (2 <= irq <= %d)\n", - 0xffff-SER12_EXTENT, nr_irqs); - return -ENXIO; - } - if (bc->baud < 300 || bc->baud > 4800) { - printk(KERN_INFO "baycom_ser_fdx: invalid baudrate " - "(300...4800)\n"); - return -EINVAL; - } - if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) { - printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy\n", - dev->base_addr); - return -EACCES; - } - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = bc->baud; - bc->baud_us = 1000000/bc->baud; - bc->baud_uartdiv = (115200/8)/bc->baud; - if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown){ - release_region(dev->base_addr, SER12_EXTENT); - return -EIO; - } - outb(0, FCR(dev->base_addr)); /* disable FIFOs */ - outb(0x0d, MCR(dev->base_addr)); - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, - "baycom_ser_fdx", dev)) { - release_region(dev->base_addr, SER12_EXTENT); - return -EBUSY; - } - /* - * set the SIO to 6 Bits/character; during receive, - * the baud rate is set to produce 100 ints/sec - * to feed the channel arbitration process, - * during transmit to baud ints/sec to run - * the transmitter - */ - ser12_set_divisor(dev, 115200/100/8); - /* - * enable transmitter empty interrupt and modem status interrupt - */ - outb(0x0a, IER(dev->base_addr)); - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - hdlcdrv_setdcd(&bc->hdrv, 0); - printk(KERN_INFO "%s: ser_fdx at iobase 0x%lx irq %u baud %u uart %s\n", - bc_drvname, dev->base_addr, dev->irq, bc->baud, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (!dev || !bc) - return -EINVAL; - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - free_irq(dev->irq, dev); - release_region(dev->base_addr, SER12_EXTENT); - printk(KERN_INFO "%s: close ser_fdx at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops ser12_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = ser12_open, - .close = ser12_close, - .ioctl = baycom_ioctl, -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - unsigned int baud; - - if (!strncmp(modestr, "ser", 3)) { - baud = simple_strtoul(modestr+3, NULL, 10); - if (baud >= 3 && baud <= 48) - bc->baud = baud*100; - } - if (strchr(modestr, '*')) - bc->opt_dcd = 0; - else if (strchr(modestr, '+')) - bc->opt_dcd = -1; - else - bc->opt_dcd = 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - sprintf(hi->data.modename, "ser%u", bc->baud / 100); - if (bc->opt_dcd <= 0) - strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : "+"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "ser12,ser3,ser24"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "ser12*", }; -static int iobase[NR_PORTS] = { 0x3f8, }; -static int irq[NR_PORTS] = { 4, }; -static int baud[NR_PORTS] = { [0 ... NR_PORTS-1] = 1200 }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "baycom irq number"); -module_param_array(baud, int, NULL, 0); -MODULE_PARM_DESC(baud, "baycom baud rate (300 to 4800)"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int __init init_baycomserfdx(void) -{ - int i, found = 0; - char set_hw = 1; - - printk(bc_drvinfo); - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcsf%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = irq[i] = 0; - - dev = hdlcdrv_register(&ser12_ops, - sizeof(struct baycom_state), - ifname, iobase[i], irq[i], 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - bc->baud = baud[i]; - found++; - baycom_device[i] = dev; - } - - if (!found) - return -ENXIO; - return 0; -} - -static void __exit cleanup_baycomserfdx(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - if (dev) - hdlcdrv_unregister(dev); - } -} - -module_init(init_baycomserfdx); -module_exit(cleanup_baycomserfdx); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_ser_fdx=io,irq,mode - * mode: ser# hardware DCD - * ser#* software DCD - * ser#+ hardware DCD, inverted signal at DCD pin - * '#' denotes the baud rate / 100, eg. ser12* is '1200 baud, soft DCD' - */ - -static int __init baycom_ser_fdx_setup(char *str) -{ - static unsigned nr_dev; - int ints[4]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 4, ints); - if (ints[0] < 2) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - irq[nr_dev] = ints[2]; - if (ints[0] >= 3) - baud[nr_dev] = ints[3]; - nr_dev++; - return 1; -} - -__setup("baycom_ser_fdx=", baycom_ser_fdx_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c deleted file mode 100644 index 05bdad214799..000000000000 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ /dev/null @@ -1,727 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * baycom_ser_hdx.c -- baycom ser12 halfduplex radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * Supported modems - * - * ser12: This is a very simple 1200 baud AFSK modem. The modem consists only - * of a modulator/demodulator chip, usually a TI TCM3105. The computer - * is responsible for regenerating the receiver bit clock, as well as - * for handling the HDLC protocol. The modem connects to a serial port, - * hence the name. Since the serial port is not used as an async serial - * port, the kernel driver for serial ports cannot be used, and this - * driver only supports standard serial hardware (8250, 16450, 16550A) - * - * Command line options (insmod command line) - * - * mode ser12 hardware DCD - * ser12* software DCD - * ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware - * mutes audio input to the modem - * ser12+ hardware DCD, inverted signal at DCD pin - * iobase base address of the port; common values are 0x3f8, 0x2f8, 0x3e8, 0x2e8 - * irq interrupt line of the port; common values are 4,3 - * - * History: - * 0.1 26.06.1996 Adapted from baycom.c and made network driver interface - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.3 26.04.1997 init code/data tagged - * 0.4 08.07.1997 alternative ser12 decoding algorithm (uses delta CTS ints) - * 0.5 11.11.1997 ser12/par96 split into separate files - * 0.6 14.04.1998 cleanups - * 0.7 03.08.1999 adapt to Linus' new __setup/__initcall - * 0.8 10.08.1999 use module_init/module_exit - * 0.9 12.02.2000 adapted to softnet driver interface - * 0.10 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include <linux/capability.h> -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/uaccess.h> -#include <asm/io.h> -#include <linux/hdlcdrv.h> -#include <linux/baycom.h> -#include <linux/jiffies.h> - -/* --------------------------------------------------------------------- */ - -#define BAYCOM_DEBUG - -/* --------------------------------------------------------------------- */ - -static const char bc_drvname[] = "baycom_ser_hdx"; -static const char bc_drvinfo[] = KERN_INFO "baycom_ser_hdx: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -"baycom_ser_hdx: version 0.10\n"; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device *baycom_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define SER12_EXTENT 8 - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct baycom_state { - struct hdlcdrv_state hdrv; - - int opt_dcd; - - struct modem_state { - short arb_divider; - unsigned char flags; - unsigned int shreg; - struct modem_state_ser12 { - unsigned char tx_bit; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned char last_sample; - unsigned char last_rxbit; - unsigned int dcd_shreg; - unsigned int dcd_time; - unsigned int bit_pll; - unsigned char interm_sample; - } ser12; - } modem; - -#ifdef BAYCOM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - int cur_pllcorr; - int last_pllcorr; - } debug_vals; -#endif /* BAYCOM_DEBUG */ -}; - -/* --------------------------------------------------------------------- */ - -static inline void baycom_int_freq(struct baycom_state *bc) -{ -#ifdef BAYCOM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - bc->debug_vals.cur_intcnt++; - if (time_after_eq(cur_jiffies, bc->debug_vals.last_jiffies + HZ)) { - bc->debug_vals.last_jiffies = cur_jiffies; - bc->debug_vals.last_intcnt = bc->debug_vals.cur_intcnt; - bc->debug_vals.cur_intcnt = 0; - bc->debug_vals.last_pllcorr = bc->debug_vals.cur_pllcorr; - bc->debug_vals.cur_pllcorr = 0; - } -#endif /* BAYCOM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== SER12 specific routines ========================= - */ - -static inline void ser12_set_divisor(struct net_device *dev, - unsigned char divisor) -{ - outb(0x81, LCR(dev->base_addr)); /* DLAB = 1 */ - outb(divisor, DLL(dev->base_addr)); - outb(0, DLM(dev->base_addr)); - outb(0x01, LCR(dev->base_addr)); /* word length = 6 */ - /* - * make sure the next interrupt is generated; - * 0 must be used to power the modem; the modem draws its - * power from the TxD line - */ - outb(0x00, THR(dev->base_addr)); - /* - * it is important not to set the divider while transmitting; - * this reportedly makes some UARTs generating interrupts - * in the hundredthousands per second region - * Reported by: Ignacio.Arenaza@studi.epfl.ch (Ignacio Arenaza Nuno) - */ -} - -/* --------------------------------------------------------------------- */ - -/* - * must call the TX arbitrator every 10ms - */ -#define SER12_ARB_DIVIDER(bc) (bc->opt_dcd ? 24 : 36) - -#define SER12_DCD_INTERVAL(bc) (bc->opt_dcd ? 12 : 240) - -static inline void ser12_tx(struct net_device *dev, struct baycom_state *bc) -{ - /* one interrupt per channel bit */ - ser12_set_divisor(dev, 12); - /* - * first output the last bit (!) then call HDLC transmitter, - * since this may take quite long - */ - outb(0x0e | (!!bc->modem.ser12.tx_bit), MCR(dev->base_addr)); - if (bc->modem.shreg <= 1) - bc->modem.shreg = 0x10000 | hdlcdrv_getbits(&bc->hdrv); - bc->modem.ser12.tx_bit = !(bc->modem.ser12.tx_bit ^ - (bc->modem.shreg & 1)); - bc->modem.shreg >>= 1; -} - -/* --------------------------------------------------------------------- */ - -static inline void ser12_rx(struct net_device *dev, struct baycom_state *bc) -{ - unsigned char cur_s; - /* - * do demodulator - */ - cur_s = inb(MSR(dev->base_addr)) & 0x10; /* the CTS line */ - hdlcdrv_channelbit(&bc->hdrv, cur_s); - bc->modem.ser12.dcd_shreg = (bc->modem.ser12.dcd_shreg << 1) | - (cur_s != bc->modem.ser12.last_sample); - bc->modem.ser12.last_sample = cur_s; - if(bc->modem.ser12.dcd_shreg & 1) { - if (!bc->opt_dcd) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - if (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - dcdspos += 2; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } else - bc->modem.ser12.dcd_sum0--; - } - if(!bc->modem.ser12.dcd_time) { - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; - if (!bc->opt_dcd) { - /* - * PLL code for the improved software DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(dev, 4); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 7) { - case 1: /* transition too late */ - ser12_set_divisor(dev, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 4: /* transition too early */ - ser12_set_divisor(dev, 3); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(dev, 4); - break; - } - bc->modem.shreg >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->modem.shreg |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - if (++bc->modem.ser12.interm_sample >= 3) - bc->modem.ser12.interm_sample = 0; - /* - * DCD stuff - */ - if (bc->modem.ser12.dcd_shreg & 1) { - unsigned int dcdspos, dcdsneg; - - dcdspos = dcdsneg = 0; - dcdspos += ((bc->modem.ser12.dcd_shreg >> 1) & 1); - dcdspos += (!(bc->modem.ser12.dcd_shreg & 0x7ffffffe)) - << 1; - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 2) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 3) & 1); - dcdsneg += ((bc->modem.ser12.dcd_shreg >> 4) & 1); - - bc->modem.ser12.dcd_sum0 += 16*dcdspos - dcdsneg; - } - } else { - /* - * PLL algorithm for the hardware squelch DCD algorithm - */ - if (bc->modem.ser12.interm_sample) { - /* - * intermediate sample; set timing correction to normal - */ - ser12_set_divisor(dev, 6); - } else { - /* - * do PLL correction and call HDLC receiver - */ - switch (bc->modem.ser12.dcd_shreg & 3) { - case 1: /* transition too late */ - ser12_set_divisor(dev, 7); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr++; -#endif /* BAYCOM_DEBUG */ - break; - case 2: /* transition too early */ - ser12_set_divisor(dev, 5); -#ifdef BAYCOM_DEBUG - bc->debug_vals.cur_pllcorr--; -#endif /* BAYCOM_DEBUG */ - break; - default: - ser12_set_divisor(dev, 6); - break; - } - bc->modem.shreg >>= 1; - if (bc->modem.ser12.last_sample == - bc->modem.ser12.last_rxbit) - bc->modem.shreg |= 0x10000; - bc->modem.ser12.last_rxbit = - bc->modem.ser12.last_sample; - } - bc->modem.ser12.interm_sample = !bc->modem.ser12.interm_sample; - /* - * DCD stuff - */ - bc->modem.ser12.dcd_sum0 -= (bc->modem.ser12.dcd_shreg & 1); - } - outb(0x0d, MCR(dev->base_addr)); /* transmitter off */ - if (bc->modem.shreg & 1) { - hdlcdrv_putbits(&bc->hdrv, bc->modem.shreg >> 1); - bc->modem.shreg = 0x10000; - } - if(!bc->modem.ser12.dcd_time) { - if (bc->opt_dcd & 1) - hdlcdrv_setdcd(&bc->hdrv, !((inb(MSR(dev->base_addr)) ^ bc->opt_dcd) & 0x80)); - else - hdlcdrv_setdcd(&bc->hdrv, (bc->modem.ser12.dcd_sum0 + - bc->modem.ser12.dcd_sum1 + - bc->modem.ser12.dcd_sum2) < 0); - bc->modem.ser12.dcd_sum2 = bc->modem.ser12.dcd_sum1; - bc->modem.ser12.dcd_sum1 = bc->modem.ser12.dcd_sum0; - /* offset to ensure DCD off on silent input */ - bc->modem.ser12.dcd_sum0 = 2; - bc->modem.ser12.dcd_time = SER12_DCD_INTERVAL(bc); - } - bc->modem.ser12.dcd_time--; -} - -/* --------------------------------------------------------------------- */ - -static irqreturn_t ser12_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct baycom_state *bc = netdev_priv(dev); - unsigned char iir; - - if (!dev || !bc || bc->hdrv.magic != HDLCDRV_MAGIC) - return IRQ_NONE; - /* fast way out */ - if ((iir = inb(IIR(dev->base_addr))) & 1) - return IRQ_NONE; - baycom_int_freq(bc); - do { - switch (iir & 6) { - case 6: - inb(LSR(dev->base_addr)); - break; - - case 4: - inb(RBR(dev->base_addr)); - break; - - case 2: - /* - * check if transmitter active - */ - if (hdlcdrv_ptt(&bc->hdrv)) - ser12_tx(dev, bc); - else { - ser12_rx(dev, bc); - bc->modem.arb_divider--; - } - outb(0x00, THR(dev->base_addr)); - break; - - default: - inb(MSR(dev->base_addr)); - break; - } - iir = inb(IIR(dev->base_addr)); - } while (!(iir & 1)); - if (bc->modem.arb_divider <= 0) { - bc->modem.arb_divider = SER12_ARB_DIVIDER(bc); - local_irq_enable(); - hdlcdrv_arbitrate(dev, &bc->hdrv); - } - local_irq_enable(); - hdlcdrv_transmitter(dev, &bc->hdrv); - hdlcdrv_receiver(dev, &bc->hdrv); - local_irq_disable(); - return IRQ_HANDLED; -} - -/* --------------------------------------------------------------------- */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = { - "unknown", "8250", "16450", "16550", "16550A" -}; - -static enum uart ser12_check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_open(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - enum uart u; - - if (!dev || !bc) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || - dev->irq < 2 || dev->irq > 15) - return -ENXIO; - if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser12")) - return -EACCES; - memset(&bc->modem, 0, sizeof(bc->modem)); - bc->hdrv.par.bitrate = 1200; - if ((u = ser12_check_uart(dev->base_addr)) == c_uart_unknown) { - release_region(dev->base_addr, SER12_EXTENT); - return -EIO; - } - outb(0, FCR(dev->base_addr)); /* disable FIFOs */ - outb(0x0d, MCR(dev->base_addr)); - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, - "baycom_ser12", dev)) { - release_region(dev->base_addr, SER12_EXTENT); - return -EBUSY; - } - /* - * enable transmitter empty interrupt - */ - outb(2, IER(dev->base_addr)); - /* - * set the SIO to 6 Bits/character and 19200 or 28800 baud, so that - * we get exactly (hopefully) 2 or 3 interrupts per radio symbol, - * depending on the usage of the software DCD routine - */ - ser12_set_divisor(dev, bc->opt_dcd ? 6 : 4); - printk(KERN_INFO "%s: ser12 at iobase 0x%lx irq %u uart %s\n", - bc_drvname, dev->base_addr, dev->irq, uart_str[u]); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int ser12_close(struct net_device *dev) -{ - struct baycom_state *bc = netdev_priv(dev); - - if (!dev || !bc) - return -EINVAL; - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - free_irq(dev->irq, dev); - release_region(dev->base_addr, SER12_EXTENT); - printk(KERN_INFO "%s: close ser12 at iobase 0x%lx irq %u\n", - bc_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== hdlcdrv driver interface ========================= - */ - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops ser12_ops = { - .drvname = bc_drvname, - .drvinfo = bc_drvinfo, - .open = ser12_open, - .close = ser12_close, - .ioctl = baycom_ioctl, -}; - -/* --------------------------------------------------------------------- */ - -static int baycom_setmode(struct baycom_state *bc, const char *modestr) -{ - if (strchr(modestr, '*')) - bc->opt_dcd = 0; - else if (strchr(modestr, '+')) - bc->opt_dcd = -1; - else if (strchr(modestr, '@')) - bc->opt_dcd = -2; - else - bc->opt_dcd = 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int baycom_ioctl(struct net_device *dev, void __user *data, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct baycom_state *bc; - struct baycom_ioctl bi; - - if (!dev) - return -EINVAL; - - bc = netdev_priv(dev); - BUG_ON(bc->hdrv.magic != HDLCDRV_MAGIC); - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - switch (hi->cmd) { - default: - break; - - case HDLCDRVCTL_GETMODE: - strscpy(hi->data.modename, "ser12"); - if (bc->opt_dcd <= 0) - strcat(hi->data.modename, (!bc->opt_dcd) ? "*" : (bc->opt_dcd == -2) ? "@" : "+"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return baycom_setmode(bc, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - strscpy(hi->data.modename, "ser12"); - if (copy_to_user(data, hi, sizeof(struct hdlcdrv_ioctl))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_MODEMPARMASK: - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ; - - } - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - -#ifdef BAYCOM_DEBUG - case BAYCOMCTL_GETDEBUG: - bi.data.dbg.debug1 = bc->hdrv.ptt_keyed; - bi.data.dbg.debug2 = bc->debug_vals.last_intcnt; - bi.data.dbg.debug3 = bc->debug_vals.last_pllcorr; - break; -#endif /* BAYCOM_DEBUG */ - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { "ser12*", }; -static int iobase[NR_PORTS] = { 0x3f8, }; -static int irq[NR_PORTS] = { 4, }; - -module_param_array(mode, charp, NULL, 0); -MODULE_PARM_DESC(mode, "baycom operating mode; * for software DCD"); -module_param_hw_array(iobase, int, ioport, NULL, 0); -MODULE_PARM_DESC(iobase, "baycom io base address"); -module_param_hw_array(irq, int, irq, NULL, 0); -MODULE_PARM_DESC(irq, "baycom irq number"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver"); -MODULE_LICENSE("GPL"); - -/* --------------------------------------------------------------------- */ - -static int __init init_baycomserhdx(void) -{ - int i, found = 0; - char set_hw = 1; - - printk(bc_drvinfo); - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev; - struct baycom_state *bc; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "bcsh%d", i); - - if (!mode[i]) - set_hw = 0; - if (!set_hw) - iobase[i] = irq[i] = 0; - - dev = hdlcdrv_register(&ser12_ops, - sizeof(struct baycom_state), - ifname, iobase[i], irq[i], 0); - if (IS_ERR(dev)) - break; - - bc = netdev_priv(dev); - if (set_hw && baycom_setmode(bc, mode[i])) - set_hw = 0; - found++; - baycom_device[i] = dev; - } - - if (!found) - return -ENXIO; - return 0; -} - -static void __exit cleanup_baycomserhdx(void) -{ - int i; - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = baycom_device[i]; - - if (dev) - hdlcdrv_unregister(dev); - } -} - -module_init(init_baycomserhdx); -module_exit(cleanup_baycomserhdx); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: baycom_ser_hdx=io,irq,mode - * mode: ser12 hardware DCD - * ser12* software DCD - * ser12@ hardware/software DCD, i.e. no explicit DCD signal but hardware - * mutes audio input to the modem - * ser12+ hardware DCD, inverted signal at DCD pin - */ - -static int __init baycom_ser_hdx_setup(char *str) -{ - static unsigned nr_dev; - int ints[3]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 3, ints); - if (ints[0] < 2) - return 0; - mode[nr_dev] = str; - iobase[nr_dev] = ints[1]; - irq[nr_dev] = ints[2]; - nr_dev++; - return 1; -} - -__setup("baycom_ser_hdx=", baycom_ser_hdx_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c deleted file mode 100644 index 214fd1f819a1..000000000000 --- a/drivers/net/hamradio/bpqether.c +++ /dev/null @@ -1,593 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * G8BPQ compatible "AX.25 via ethernet" driver release 004 - * - * This code REQUIRES 2.0.0 or higher/ NET3.029 - * - * This is a "pseudo" network driver to allow AX.25 over Ethernet - * using G8BPQ encapsulation. It has been extracted from the protocol - * implementation because - * - * - things got unreadable within the protocol stack - * - to cure the protocol stack from "feature-ism" - * - a protocol implementation shouldn't need to know on - * which hardware it is running - * - user-level programs like the AX.25 utilities shouldn't - * need to know about the hardware. - * - IP over ethernet encapsulated AX.25 was impossible - * - rxecho.c did not work - * - to have room for extensions - * - it just deserves to "live" as an own driver - * - * This driver can use any ethernet destination address, and can be - * limited to accept frames from one dedicated ethernet card only. - * - * Note that the driver sets up the BPQ devices automagically on - * startup or (if started before the "insmod" of an ethernet device) - * on "ifconfig up". It hopefully will remove the BPQ on "rmmod"ing - * the ethernet device (in fact: as soon as another ethernet or bpq - * device gets "ifconfig"ured). - * - * I have heard that several people are thinking of experiments - * with highspeed packet radio using existing ethernet cards. - * Well, this driver is prepared for this purpose, just add - * your tx key control and a txdelay / tailtime algorithm, - * probably some buffering, and /voila/... - * - * History - * BPQ 001 Joerg(DL1BKE) Extracted BPQ code from AX.25 - * protocol stack and added my own - * yet existing patches - * BPQ 002 Joerg(DL1BKE) Scan network device list on - * startup. - * BPQ 003 Joerg(DL1BKE) Ethernet destination address - * and accepted source address - * can be configured by an ioctl() - * call. - * Fixed to match Linux networking - * changes - 2.1.15. - * BPQ 004 Joerg(DL1BKE) Fixed to not lock up on ifconfig. - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/net.h> -#include <linux/slab.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <linux/uaccess.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/stat.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/rtnetlink.h> - -#include <net/ip.h> -#include <net/arp.h> -#include <net/netdev_lock.h> -#include <net/net_namespace.h> - -#include <linux/bpqether.h> - -static const char banner[] __initconst = KERN_INFO \ - "AX.25: bpqether driver version 004\n"; - -static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); -static int bpq_device_event(struct notifier_block *, unsigned long, void *); - -static struct packet_type bpq_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_BPQ), - .func = bpq_rcv, -}; - -static struct notifier_block bpq_dev_notifier = { - .notifier_call = bpq_device_event, -}; - - -struct bpqdev { - struct list_head bpq_list; /* list of bpq devices chain */ - struct net_device *ethdev; /* link to ethernet device */ - struct net_device *axdev; /* bpq device (bpq#) */ - char dest_addr[6]; /* ether destination address */ - char acpt_addr[6]; /* accept ether frames from this address only */ -}; - -static LIST_HEAD(bpq_devices); - -/* ------------------------------------------------------------------------ */ - - -/* - * Get the ethernet device for a BPQ device - */ -static inline struct net_device *bpq_get_ether_dev(struct net_device *dev) -{ - struct bpqdev *bpq = netdev_priv(dev); - - return bpq ? bpq->ethdev : NULL; -} - -/* - * Get the BPQ device for the ethernet device - */ -static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev) -{ - struct bpqdev *bpq; - - list_for_each_entry_rcu(bpq, &bpq_devices, bpq_list, - lockdep_rtnl_is_held()) { - if (bpq->ethdev == dev) - return bpq->axdev; - } - return NULL; -} - -static inline int dev_is_ethdev(struct net_device *dev) -{ - return dev->type == ARPHRD_ETHER && !netdev_need_ops_lock(dev); -} - -/* ------------------------------------------------------------------------ */ - - -/* - * Receive an AX.25 frame via an ethernet interface. - */ -static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) -{ - int len; - char * ptr; - struct ethhdr *eth; - struct bpqdev *bpq; - - if (!net_eq(dev_net(dev), &init_net)) - goto drop; - - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) - return NET_RX_DROP; - - if (!pskb_may_pull(skb, sizeof(struct ethhdr))) - goto drop; - - rcu_read_lock(); - dev = bpq_get_ax25_dev(dev); - - if (dev == NULL || !netif_running(dev)) - goto drop_unlock; - - /* - * if we want to accept frames from just one ethernet device - * we check the source address of the sender. - */ - - bpq = netdev_priv(dev); - - eth = eth_hdr(skb); - - if (!(bpq->acpt_addr[0] & 0x01) && - !ether_addr_equal(eth->h_source, bpq->acpt_addr)) - goto drop_unlock; - - if (skb_cow(skb, sizeof(struct ethhdr))) - goto drop_unlock; - - len = skb->data[0] + skb->data[1] * 256 - 5; - - if (len < 0 || len > skb->len - 2) - goto drop_unlock; - - skb_pull(skb, 2); /* Remove the length bytes */ - skb_trim(skb, len); /* Set the length of the data */ - - dev->stats.rx_packets++; - dev->stats.rx_bytes += len; - - ptr = skb_push(skb, 1); - *ptr = 0; - - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); -unlock: - - rcu_read_unlock(); - - return 0; -drop_unlock: - kfree_skb(skb); - goto unlock; - -drop: - kfree_skb(skb); - return 0; -} - -/* - * Send an AX.25 frame via an ethernet interface - */ -static netdev_tx_t bpq_xmit(struct sk_buff *skb, struct net_device *dev) -{ - unsigned char *ptr; - struct bpqdev *bpq; - struct net_device *orig_dev; - int size; - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - /* - * Just to be *really* sure not to send anything if the interface - * is down, the ethernet device may have gone. - */ - if (!netif_running(dev)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb_pull(skb, 1); /* Drop KISS byte */ - size = skb->len; - - /* - * We're about to mess with the skb which may still shared with the - * generic networking code so unshare and ensure it's got enough - * space for the BPQ headers. - */ - if (skb_cow(skb, AX25_BPQ_HEADER_LEN)) { - if (net_ratelimit()) - pr_err("bpqether: out of memory\n"); - kfree_skb(skb); - - return NETDEV_TX_OK; - } - - ptr = skb_push(skb, 2); /* Make space for length */ - - *ptr++ = (size + 5) % 256; - *ptr++ = (size + 5) / 256; - - bpq = netdev_priv(dev); - - orig_dev = dev; - if ((dev = bpq_get_ether_dev(dev)) == NULL) { - orig_dev->stats.tx_dropped++; - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb->protocol = ax25_type_trans(skb, dev); - skb_reset_network_header(skb); - dev_hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); - dev->stats.tx_packets++; - dev->stats.tx_bytes+=skb->len; - - dev_queue_xmit(skb); - netif_wake_queue(dev); - return NETDEV_TX_OK; -} - -/* - * Set AX.25 callsign - */ -static int bpq_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - dev_addr_set(dev, sa->sa_data); - - return 0; -} - -/* Ioctl commands - * - * SIOCSBPQETHOPT reserved for enhancements - * SIOCSBPQETHADDR set the destination and accepted - * source ethernet address (broadcast - * or multicast: accept all) - */ -static int bpq_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct bpq_ethaddr __user *ethaddr = data; - struct bpqdev *bpq = netdev_priv(dev); - struct bpq_req req; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - switch (cmd) { - case SIOCSBPQETHOPT: - if (copy_from_user(&req, data, sizeof(struct bpq_req))) - return -EFAULT; - switch (req.cmd) { - case SIOCGBPQETHPARAM: - case SIOCSBPQETHPARAM: - default: - return -EINVAL; - } - - break; - - case SIOCSBPQETHADDR: - if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN)) - return -EFAULT; - if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN)) - return -EFAULT; - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* - * open/close a device - */ -static int bpq_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int bpq_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - - -/* ------------------------------------------------------------------------ */ - -#ifdef CONFIG_PROC_FS -/* - * Proc filesystem - */ -static void *bpq_seq_start(struct seq_file *seq, loff_t *pos) - __acquires(RCU) -{ - int i = 1; - struct bpqdev *bpqdev; - - rcu_read_lock(); - - if (*pos == 0) - return SEQ_START_TOKEN; - - list_for_each_entry_rcu(bpqdev, &bpq_devices, bpq_list) { - if (i == *pos) - return bpqdev; - } - return NULL; -} - -static void *bpq_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct list_head *p; - struct bpqdev *bpqdev = v; - - ++*pos; - - if (v == SEQ_START_TOKEN) - p = rcu_dereference(list_next_rcu(&bpq_devices)); - else - p = rcu_dereference(list_next_rcu(&bpqdev->bpq_list)); - - return (p == &bpq_devices) ? NULL - : list_entry(p, struct bpqdev, bpq_list); -} - -static void bpq_seq_stop(struct seq_file *seq, void *v) - __releases(RCU) -{ - rcu_read_unlock(); -} - - -static int bpq_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, - "dev ether destination accept from\n"); - else { - const struct bpqdev *bpqdev = v; - - seq_printf(seq, "%-5s %-10s %pM ", - bpqdev->axdev->name, bpqdev->ethdev->name, - bpqdev->dest_addr); - - if (is_multicast_ether_addr(bpqdev->acpt_addr)) - seq_printf(seq, "*\n"); - else - seq_printf(seq, "%pM\n", bpqdev->acpt_addr); - - } - return 0; -} - -static const struct seq_operations bpq_seqops = { - .start = bpq_seq_start, - .next = bpq_seq_next, - .stop = bpq_seq_stop, - .show = bpq_seq_show, -}; -#endif -/* ------------------------------------------------------------------------ */ - -static const struct net_device_ops bpq_netdev_ops = { - .ndo_open = bpq_open, - .ndo_stop = bpq_close, - .ndo_start_xmit = bpq_xmit, - .ndo_set_mac_address = bpq_set_mac_address, - .ndo_siocdevprivate = bpq_siocdevprivate, -}; - -static void bpq_setup(struct net_device *dev) -{ - netdev_lockdep_set_classes(dev); - - dev->netdev_ops = &bpq_netdev_ops; - dev->needs_free_netdev = true; - - dev->flags = 0; - dev->lltx = true; /* Allow recursion */ - -#if IS_ENABLED(CONFIG_AX25) - dev->header_ops = &ax25_header_ops; -#endif - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -/* - * Setup a new device. - */ -static int bpq_new_device(struct net_device *edev) -{ - int err; - struct net_device *ndev; - struct bpqdev *bpq; - - ndev = alloc_netdev(sizeof(struct bpqdev), "bpq%d", NET_NAME_UNKNOWN, - bpq_setup); - if (!ndev) - return -ENOMEM; - - - bpq = netdev_priv(ndev); - dev_hold(edev); - bpq->ethdev = edev; - bpq->axdev = ndev; - - eth_broadcast_addr(bpq->dest_addr); - eth_broadcast_addr(bpq->acpt_addr); - - err = register_netdevice(ndev); - if (err) - goto error; - - /* List protected by RTNL */ - list_add_rcu(&bpq->bpq_list, &bpq_devices); - return 0; - - error: - dev_put(edev); - free_netdev(ndev); - return err; - -} - -static void bpq_free_device(struct net_device *ndev) -{ - struct bpqdev *bpq = netdev_priv(ndev); - - dev_put(bpq->ethdev); - list_del_rcu(&bpq->bpq_list); - - unregister_netdevice(ndev); -} - -/* - * Handle device status changes. - */ -static int bpq_device_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - - if (!net_eq(dev_net(dev), &init_net)) - return NOTIFY_DONE; - - if (!dev_is_ethdev(dev) && !bpq_get_ax25_dev(dev)) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: /* new ethernet device -> new BPQ interface */ - if (bpq_get_ax25_dev(dev) == NULL) - bpq_new_device(dev); - break; - - case NETDEV_DOWN: /* ethernet device closed -> close BPQ interface */ - if ((dev = bpq_get_ax25_dev(dev)) != NULL) - dev_close(dev); - break; - - case NETDEV_UNREGISTER: /* ethernet device removed -> free BPQ interface */ - if ((dev = bpq_get_ax25_dev(dev)) != NULL) - bpq_free_device(dev); - break; - default: - break; - } - - return NOTIFY_DONE; -} - - -/* ------------------------------------------------------------------------ */ - -/* - * Initialize driver. To be called from af_ax25 if not compiled as a - * module - */ -static int __init bpq_init_driver(void) -{ -#ifdef CONFIG_PROC_FS - if (!proc_create_seq("bpqether", 0444, init_net.proc_net, &bpq_seqops)) { - printk(KERN_ERR - "bpq: cannot create /proc/net/bpqether entry.\n"); - return -ENOENT; - } -#endif /* CONFIG_PROC_FS */ - - dev_add_pack(&bpq_packet_type); - - register_netdevice_notifier(&bpq_dev_notifier); - - printk(banner); - - return 0; -} - -static void __exit bpq_cleanup_driver(void) -{ - struct bpqdev *bpq; - - dev_remove_pack(&bpq_packet_type); - - unregister_netdevice_notifier(&bpq_dev_notifier); - - remove_proc_entry("bpqether", init_net.proc_net); - - rtnl_lock(); - while (!list_empty(&bpq_devices)) { - bpq = list_entry(bpq_devices.next, struct bpqdev, bpq_list); - bpq_free_device(bpq->axdev); - } - rtnl_unlock(); -} - -MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@yaina.de>"); -MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet"); -MODULE_LICENSE("GPL"); -module_init(bpq_init_driver); -module_exit(bpq_cleanup_driver); diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c deleted file mode 100644 index 3b88e465d08f..000000000000 --- a/drivers/net/hamradio/hdlcdrv.c +++ /dev/null @@ -1,747 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * hdlcdrv.c -- HDLC packet radio network driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * The driver was derived from Donald Beckers skeleton.c - * Written 1993-94 by Donald Becker. - * - * History: - * 0.1 21.09.1996 Started - * 18.10.1996 Changed to new user space access routines - * (copy_{to,from}_user) - * 0.2 21.11.1996 various small changes - * 0.3 03.03.1997 fixed (hopefully) IP not working with ax.25 as a module - * 0.4 16.04.1997 init code/data tagged - * 0.5 30.07.1997 made HDLC buffers bigger (solves a problem with the - * soundmodem driver) - * 0.6 05.04.1998 add spinlocks - * 0.7 03.08.1999 removed some old compatibility cruft - * 0.8 12.02.2000 adapted to softnet driver interface - */ - -/*****************************************************************************/ - -#include <linux/capability.h> -#include <linux/compat.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/net.h> -#include <linux/in.h> -#include <linux/if.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/bitops.h> - -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <linux/hdlcdrv.h> -#include <linux/random.h> -#include <net/ax25.h> -#include <linux/uaccess.h> - -#include <linux/crc-ccitt.h> - -/* --------------------------------------------------------------------- */ - -#define KISS_VERBOSE - -/* --------------------------------------------------------------------- */ - -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_TXTAIL 4 -#define PARAM_FULLDUP 5 -#define PARAM_HARDWARE 6 -#define PARAM_RETURN 255 - -/* --------------------------------------------------------------------- */ -/* - * the CRC routines are stolen from WAMPES - * by Dieter Deyke - */ - - -/*---------------------------------------------------------------------------*/ - -static inline void append_crc_ccitt(unsigned char *buffer, int len) -{ - unsigned int crc = crc_ccitt(0xffff, buffer, len) ^ 0xffff; - buffer += len; - *buffer++ = crc; - *buffer++ = crc >> 8; -} - -/*---------------------------------------------------------------------------*/ - -static inline int check_crc_ccitt(const unsigned char *buf, int cnt) -{ - return (crc_ccitt(0xffff, buf, cnt) & 0xffff) == 0xf0b8; -} - -/*---------------------------------------------------------------------------*/ - -#if 0 -static int calc_crc_ccitt(const unsigned char *buf, int cnt) -{ - unsigned int crc = 0xffff; - - for (; cnt > 0; cnt--) - crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ *buf++) & 0xff]; - crc ^= 0xffff; - return crc & 0xffff; -} -#endif - -/* ---------------------------------------------------------------------- */ - -#define tenms_to_2flags(s,tenms) ((tenms * s->par.bitrate) / 100 / 16) - -/* ---------------------------------------------------------------------- */ -/* - * The HDLC routines - */ - -static int hdlc_rx_add_bytes(struct hdlcdrv_state *s, unsigned int bits, - int num) -{ - int added = 0; - - while (s->hdlcrx.rx_state && num >= 8) { - if (s->hdlcrx.len >= sizeof(s->hdlcrx.buffer)) { - s->hdlcrx.rx_state = 0; - return 0; - } - *s->hdlcrx.bp++ = bits >> (32-num); - s->hdlcrx.len++; - num -= 8; - added += 8; - } - return added; -} - -static void hdlc_rx_flag(struct net_device *dev, struct hdlcdrv_state *s) -{ - struct sk_buff *skb; - int pkt_len; - unsigned char *cp; - - if (s->hdlcrx.len < 4) - return; - if (!check_crc_ccitt(s->hdlcrx.buffer, s->hdlcrx.len)) - return; - pkt_len = s->hdlcrx.len - 2 + 1; /* KISS kludge */ - if (!(skb = dev_alloc_skb(pkt_len))) { - printk("%s: memory squeeze, dropping packet\n", dev->name); - dev->stats.rx_dropped++; - return; - } - cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, s->hdlcrx.buffer, pkt_len - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - dev->stats.rx_packets++; -} - -void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s) -{ - int i; - unsigned int mask1, mask2, mask3, mask4, mask5, mask6, word; - - if (!s || s->magic != HDLCDRV_MAGIC) - return; - if (test_and_set_bit(0, &s->hdlcrx.in_hdlc_rx)) - return; - - while (!hdlcdrv_hbuf_empty(&s->hdlcrx.hbuf)) { - word = hdlcdrv_hbuf_get(&s->hdlcrx.hbuf); - -#ifdef HDLCDRV_DEBUG - hdlcdrv_add_bitbuffer_word(&s->bitbuf_hdlc, word); -#endif /* HDLCDRV_DEBUG */ - s->hdlcrx.bitstream >>= 16; - s->hdlcrx.bitstream |= word << 16; - s->hdlcrx.bitbuf >>= 16; - s->hdlcrx.bitbuf |= word << 16; - s->hdlcrx.numbits += 16; - for(i = 15, mask1 = 0x1fc00, mask2 = 0x1fe00, mask3 = 0x0fc00, - mask4 = 0x1f800, mask5 = 0xf800, mask6 = 0xffff; - i >= 0; - i--, mask1 <<= 1, mask2 <<= 1, mask3 <<= 1, mask4 <<= 1, - mask5 <<= 1, mask6 = (mask6 << 1) | 1) { - if ((s->hdlcrx.bitstream & mask1) == mask1) - s->hdlcrx.rx_state = 0; /* abort received */ - else if ((s->hdlcrx.bitstream & mask2) == mask3) { - /* flag received */ - if (s->hdlcrx.rx_state) { - hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf - << (8+i), - s->hdlcrx.numbits - -8-i); - hdlc_rx_flag(dev, s); - } - s->hdlcrx.len = 0; - s->hdlcrx.bp = s->hdlcrx.buffer; - s->hdlcrx.rx_state = 1; - s->hdlcrx.numbits = i; - } else if ((s->hdlcrx.bitstream & mask4) == mask5) { - /* stuffed bit */ - s->hdlcrx.numbits--; - s->hdlcrx.bitbuf = (s->hdlcrx.bitbuf & (~mask6)) | - ((s->hdlcrx.bitbuf & mask6) << 1); - } - } - s->hdlcrx.numbits -= hdlc_rx_add_bytes(s, s->hdlcrx.bitbuf, - s->hdlcrx.numbits); - } - clear_bit(0, &s->hdlcrx.in_hdlc_rx); -} - -/* ---------------------------------------------------------------------- */ - -static inline void do_kiss_params(struct hdlcdrv_state *s, - unsigned char *data, unsigned long len) -{ - -#ifdef KISS_VERBOSE -#define PKP(a,b) printk(KERN_INFO "hdlcdrv.c: channel params: " a "\n", b) -#else /* KISS_VERBOSE */ -#define PKP(a,b) -#endif /* KISS_VERBOSE */ - - if (len < 2) - return; - switch(data[0]) { - case PARAM_TXDELAY: - s->ch_params.tx_delay = data[1]; - PKP("TX delay = %ums", 10 * s->ch_params.tx_delay); - break; - case PARAM_PERSIST: - s->ch_params.ppersist = data[1]; - PKP("p persistence = %u", s->ch_params.ppersist); - break; - case PARAM_SLOTTIME: - s->ch_params.slottime = data[1]; - PKP("slot time = %ums", s->ch_params.slottime); - break; - case PARAM_TXTAIL: - s->ch_params.tx_tail = data[1]; - PKP("TX tail = %ums", s->ch_params.tx_tail); - break; - case PARAM_FULLDUP: - s->ch_params.fulldup = !!data[1]; - PKP("%s duplex", s->ch_params.fulldup ? "full" : "half"); - break; - default: - break; - } -#undef PKP -} - -/* ---------------------------------------------------------------------- */ - -void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s) -{ - unsigned int mask1, mask2, mask3; - int i; - struct sk_buff *skb; - int pkt_len; - - if (!s || s->magic != HDLCDRV_MAGIC) - return; - if (test_and_set_bit(0, &s->hdlctx.in_hdlc_tx)) - return; - for (;;) { - if (s->hdlctx.numbits >= 16) { - if (hdlcdrv_hbuf_full(&s->hdlctx.hbuf)) { - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - } - hdlcdrv_hbuf_put(&s->hdlctx.hbuf, s->hdlctx.bitbuf); - s->hdlctx.bitbuf >>= 16; - s->hdlctx.numbits -= 16; - } - switch (s->hdlctx.tx_state) { - default: - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - case 0: - case 1: - if (s->hdlctx.numflags) { - s->hdlctx.numflags--; - s->hdlctx.bitbuf |= - 0x7e7e << s->hdlctx.numbits; - s->hdlctx.numbits += 16; - break; - } - if (s->hdlctx.tx_state == 1) { - clear_bit(0, &s->hdlctx.in_hdlc_tx); - return; - } - if (!(skb = s->skb)) { - int flgs = tenms_to_2flags(s, s->ch_params.tx_tail); - if (flgs < 2) - flgs = 2; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = flgs; - break; - } - s->skb = NULL; - netif_wake_queue(dev); - pkt_len = skb->len-1; /* strip KISS byte */ - if (pkt_len >= HDLCDRV_MAXFLEN || pkt_len < 2) { - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = 1; - dev_kfree_skb_irq(skb); - break; - } - skb_copy_from_linear_data_offset(skb, 1, - s->hdlctx.buffer, - pkt_len); - dev_kfree_skb_irq(skb); - s->hdlctx.bp = s->hdlctx.buffer; - append_crc_ccitt(s->hdlctx.buffer, pkt_len); - s->hdlctx.len = pkt_len+2; /* the appended CRC */ - s->hdlctx.tx_state = 2; - s->hdlctx.bitstream = 0; - dev->stats.tx_packets++; - break; - case 2: - if (!s->hdlctx.len) { - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = 1; - break; - } - s->hdlctx.len--; - s->hdlctx.bitbuf |= *s->hdlctx.bp << - s->hdlctx.numbits; - s->hdlctx.bitstream >>= 8; - s->hdlctx.bitstream |= (*s->hdlctx.bp++) << 16; - mask1 = 0x1f000; - mask2 = 0x10000; - mask3 = 0xffffffff >> (31-s->hdlctx.numbits); - s->hdlctx.numbits += 8; - for(i = 0; i < 8; i++, mask1 <<= 1, mask2 <<= 1, - mask3 = (mask3 << 1) | 1) { - if ((s->hdlctx.bitstream & mask1) != mask1) - continue; - s->hdlctx.bitstream &= ~mask2; - s->hdlctx.bitbuf = - (s->hdlctx.bitbuf & mask3) | - ((s->hdlctx.bitbuf & - (~mask3)) << 1); - s->hdlctx.numbits++; - mask3 = (mask3 << 1) | 1; - } - break; - } - } -} - -/* ---------------------------------------------------------------------- */ - -static void start_tx(struct net_device *dev, struct hdlcdrv_state *s) -{ - s->hdlctx.tx_state = 0; - s->hdlctx.numflags = tenms_to_2flags(s, s->ch_params.tx_delay); - s->hdlctx.bitbuf = s->hdlctx.bitstream = s->hdlctx.numbits = 0; - hdlcdrv_transmitter(dev, s); - s->hdlctx.ptt = 1; - s->ptt_keyed++; -} - -/* ---------------------------------------------------------------------- */ - -void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s) -{ - if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || !s->skb) - return; - if (s->ch_params.fulldup) { - start_tx(dev, s); - return; - } - if (s->hdlcrx.dcd) { - s->hdlctx.slotcnt = s->ch_params.slottime; - return; - } - if ((--s->hdlctx.slotcnt) > 0) - return; - s->hdlctx.slotcnt = s->ch_params.slottime; - if (get_random_u8() > s->ch_params.ppersist) - return; - start_tx(dev, s); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== network driver interface ========================= - */ - -static netdev_tx_t hdlcdrv_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct hdlcdrv_state *sm = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->data[0] != 0) { - do_kiss_params(sm, skb->data, skb->len); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - if (sm->skb) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - netif_stop_queue(dev); - sm->skb = skb; - return NETDEV_TX_OK; -} - -/* --------------------------------------------------------------------- */ - -static int hdlcdrv_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *)addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ - -static int hdlcdrv_open(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - int i; - - if (!s->ops || !s->ops->open) - return -ENODEV; - - /* - * initialise some variables - */ - s->opened = 1; - s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; - s->hdlcrx.in_hdlc_rx = 0; - s->hdlcrx.rx_state = 0; - - s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; - s->hdlctx.in_hdlc_tx = 0; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = 0; - s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; - s->hdlctx.ptt = 0; - s->hdlctx.slotcnt = s->ch_params.slottime; - s->hdlctx.calibrate = 0; - - i = s->ops->open(dev); - if (i) - return i; - netif_start_queue(dev); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* - * The inverse routine to hdlcdrv_open(). - */ - -static int hdlcdrv_close(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - int i = 0; - - netif_stop_queue(dev); - - if (s->ops && s->ops->close) - i = s->ops->close(dev); - dev_kfree_skb(s->skb); - s->skb = NULL; - s->opened = 0; - return i; -} - -/* --------------------------------------------------------------------- */ - -static int hdlcdrv_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - struct hdlcdrv_ioctl bi; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (in_compat_syscall()) /* to be implemented */ - return -ENOIOCTLCMD; - - if (copy_from_user(&bi, data, sizeof(bi))) - return -EFAULT; - - switch (bi.cmd) { - default: - if (s->ops && s->ops->ioctl) - return s->ops->ioctl(dev, data, &bi, cmd); - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETCHANNELPAR: - bi.data.cp.tx_delay = s->ch_params.tx_delay; - bi.data.cp.tx_tail = s->ch_params.tx_tail; - bi.data.cp.slottime = s->ch_params.slottime; - bi.data.cp.ppersist = s->ch_params.ppersist; - bi.data.cp.fulldup = s->ch_params.fulldup; - break; - - case HDLCDRVCTL_SETCHANNELPAR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - s->ch_params.tx_delay = bi.data.cp.tx_delay; - s->ch_params.tx_tail = bi.data.cp.tx_tail; - s->ch_params.slottime = bi.data.cp.slottime; - s->ch_params.ppersist = bi.data.cp.ppersist; - s->ch_params.fulldup = bi.data.cp.fulldup; - s->hdlctx.slotcnt = 1; - return 0; - - case HDLCDRVCTL_GETMODEMPAR: - bi.data.mp.iobase = dev->base_addr; - bi.data.mp.irq = dev->irq; - bi.data.mp.dma = dev->dma; - bi.data.mp.dma2 = s->ptt_out.dma2; - bi.data.mp.seriobase = s->ptt_out.seriobase; - bi.data.mp.pariobase = s->ptt_out.pariobase; - bi.data.mp.midiiobase = s->ptt_out.midiiobase; - break; - - case HDLCDRVCTL_SETMODEMPAR: - if ((!capable(CAP_SYS_RAWIO)) || netif_running(dev)) - return -EACCES; - dev->base_addr = bi.data.mp.iobase; - dev->irq = bi.data.mp.irq; - dev->dma = bi.data.mp.dma; - s->ptt_out.dma2 = bi.data.mp.dma2; - s->ptt_out.seriobase = bi.data.mp.seriobase; - s->ptt_out.pariobase = bi.data.mp.pariobase; - s->ptt_out.midiiobase = bi.data.mp.midiiobase; - return 0; - - case HDLCDRVCTL_GETSTAT: - bi.data.cs.ptt = hdlcdrv_ptt(s); - bi.data.cs.dcd = s->hdlcrx.dcd; - bi.data.cs.ptt_keyed = s->ptt_keyed; - bi.data.cs.tx_packets = dev->stats.tx_packets; - bi.data.cs.tx_errors = dev->stats.tx_errors; - bi.data.cs.rx_packets = dev->stats.rx_packets; - bi.data.cs.rx_errors = dev->stats.rx_errors; - break; - - case HDLCDRVCTL_OLDGETSTAT: - bi.data.ocs.ptt = hdlcdrv_ptt(s); - bi.data.ocs.dcd = s->hdlcrx.dcd; - bi.data.ocs.ptt_keyed = s->ptt_keyed; - break; - - case HDLCDRVCTL_CALIBRATE: - if(!capable(CAP_SYS_RAWIO)) - return -EPERM; - if (s->par.bitrate <= 0) - return -EINVAL; - if (bi.data.calibrate > INT_MAX / s->par.bitrate) - return -EINVAL; - s->hdlctx.calibrate = bi.data.calibrate * s->par.bitrate / 16; - return 0; - - case HDLCDRVCTL_GETSAMPLES: -#ifndef HDLCDRV_DEBUG - return -EPERM; -#else /* HDLCDRV_DEBUG */ - if (s->bitbuf_channel.rd == s->bitbuf_channel.wr) - return -EAGAIN; - bi.data.bits = - s->bitbuf_channel.buffer[s->bitbuf_channel.rd]; - s->bitbuf_channel.rd = (s->bitbuf_channel.rd+1) % - sizeof(s->bitbuf_channel.buffer); - break; -#endif /* HDLCDRV_DEBUG */ - - case HDLCDRVCTL_GETBITS: -#ifndef HDLCDRV_DEBUG - return -EPERM; -#else /* HDLCDRV_DEBUG */ - if (s->bitbuf_hdlc.rd == s->bitbuf_hdlc.wr) - return -EAGAIN; - bi.data.bits = - s->bitbuf_hdlc.buffer[s->bitbuf_hdlc.rd]; - s->bitbuf_hdlc.rd = (s->bitbuf_hdlc.rd+1) % - sizeof(s->bitbuf_hdlc.buffer); - break; -#endif /* HDLCDRV_DEBUG */ - - case HDLCDRVCTL_DRIVERNAME: - if (s->ops && s->ops->drvname) { - strscpy(bi.data.drivername, s->ops->drvname, - sizeof(bi.data.drivername)); - break; - } - bi.data.drivername[0] = '\0'; - break; - - } - if (copy_to_user(data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops hdlcdrv_netdev = { - .ndo_open = hdlcdrv_open, - .ndo_stop = hdlcdrv_close, - .ndo_start_xmit = hdlcdrv_send_packet, - .ndo_siocdevprivate = hdlcdrv_siocdevprivate, - .ndo_set_mac_address = hdlcdrv_set_mac_address, -}; - -/* - * Initialize fields in hdlcdrv - */ -static void hdlcdrv_setup(struct net_device *dev) -{ - static const struct hdlcdrv_channel_params dflt_ch_params = { - 20, 2, 10, 40, 0 - }; - struct hdlcdrv_state *s = netdev_priv(dev); - - /* - * initialize the hdlcdrv_state struct - */ - s->ch_params = dflt_ch_params; - s->ptt_keyed = 0; - - spin_lock_init(&s->hdlcrx.hbuf.lock); - s->hdlcrx.hbuf.rd = s->hdlcrx.hbuf.wr = 0; - s->hdlcrx.in_hdlc_rx = 0; - s->hdlcrx.rx_state = 0; - - spin_lock_init(&s->hdlctx.hbuf.lock); - s->hdlctx.hbuf.rd = s->hdlctx.hbuf.wr = 0; - s->hdlctx.in_hdlc_tx = 0; - s->hdlctx.tx_state = 1; - s->hdlctx.numflags = 0; - s->hdlctx.bitstream = s->hdlctx.bitbuf = s->hdlctx.numbits = 0; - s->hdlctx.ptt = 0; - s->hdlctx.slotcnt = s->ch_params.slottime; - s->hdlctx.calibrate = 0; - -#ifdef HDLCDRV_DEBUG - s->bitbuf_channel.rd = s->bitbuf_channel.wr = 0; - s->bitbuf_channel.shreg = 0x80; - - s->bitbuf_hdlc.rd = s->bitbuf_hdlc.wr = 0; - s->bitbuf_hdlc.shreg = 0x80; -#endif /* HDLCDRV_DEBUG */ - - - /* Fill in the fields of the device structure */ - - s->skb = NULL; - - dev->netdev_ops = &hdlcdrv_netdev; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; /* eth_mtu is the default */ - dev->addr_len = AX25_ADDR_LEN; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - dev->tx_queue_len = 16; -} - -/* --------------------------------------------------------------------- */ -struct net_device *hdlcdrv_register(const struct hdlcdrv_ops *ops, - unsigned int privsize, const char *ifname, - unsigned int baseaddr, unsigned int irq, - unsigned int dma) -{ - struct net_device *dev; - struct hdlcdrv_state *s; - int err; - - if (privsize < sizeof(struct hdlcdrv_state)) - privsize = sizeof(struct hdlcdrv_state); - - dev = alloc_netdev(privsize, ifname, NET_NAME_UNKNOWN, hdlcdrv_setup); - if (!dev) - return ERR_PTR(-ENOMEM); - - /* - * initialize part of the hdlcdrv_state struct - */ - s = netdev_priv(dev); - s->magic = HDLCDRV_MAGIC; - s->ops = ops; - dev->base_addr = baseaddr; - dev->irq = irq; - dev->dma = dma; - - err = register_netdev(dev); - if (err < 0) { - printk(KERN_WARNING "hdlcdrv: cannot register net " - "device %s\n", dev->name); - free_netdev(dev); - dev = ERR_PTR(err); - } - return dev; -} - -/* --------------------------------------------------------------------- */ - -void hdlcdrv_unregister(struct net_device *dev) -{ - struct hdlcdrv_state *s = netdev_priv(dev); - - BUG_ON(s->magic != HDLCDRV_MAGIC); - - if (s->opened && s->ops->close) - s->ops->close(dev); - unregister_netdev(dev); - - free_netdev(dev); -} - -/* --------------------------------------------------------------------- */ - -EXPORT_SYMBOL(hdlcdrv_receiver); -EXPORT_SYMBOL(hdlcdrv_transmitter); -EXPORT_SYMBOL(hdlcdrv_arbitrate); -EXPORT_SYMBOL(hdlcdrv_register); -EXPORT_SYMBOL(hdlcdrv_unregister); - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c deleted file mode 100644 index 5f38a002bd9e..000000000000 --- a/drivers/net/hamradio/mkiss.c +++ /dev/null @@ -1,980 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * - * Copyright (C) Hans Alblas PE1AYX <hans@esrac.ele.tue.nl> - * Copyright (C) 2004, 05 Ralf Baechle DL5RB <ralf@linux-mips.org> - * Copyright (C) 2004, 05 Thomas Osterried DL9SAU <thomas@x-berg.in-berlin.de> - */ -#include <linux/module.h> -#include <linux/bitops.h> -#include <linux/uaccess.h> -#include <linux/crc16.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/inet.h> -#include <linux/slab.h> -#include <linux/tty.h> -#include <linux/errno.h> -#include <linux/netdevice.h> -#include <linux/major.h> -#include <linux/init.h> -#include <linux/rtnetlink.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/jiffies.h> -#include <linux/refcount.h> - -#include <net/ax25.h> - -#define AX_MTU 236 - -/* some arch define END as assembly function ending, just undef it */ -#undef END -/* SLIP/KISS protocol characters. */ -#define END 0300 /* indicates end of frame */ -#define ESC 0333 /* indicates byte stuffing */ -#define ESC_END 0334 /* ESC ESC_END means END 'data' */ -#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ - -struct mkiss { - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - - /* These are pointers to the malloc()ed frame buffers. */ - spinlock_t buflock;/* lock for rbuf and xbuf */ - unsigned char *rbuff; /* receiver buffer */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* pointer to next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - /* Detailed SLIP statistics. */ - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ - - unsigned long flags; /* Flag values/ mode etc */ - /* long req'd: used by set_bit --RR */ -#define AXF_INUSE 0 /* Channel in use */ -#define AXF_ESCAPE 1 /* ESC received */ -#define AXF_ERROR 2 /* Parity, etc. error */ -#define AXF_KEEPTEST 3 /* Keepalive test flag */ -#define AXF_OUTWAIT 4 /* is outpacket was flag */ - - int mode; - int crcmode; /* MW: for FlexNet, SMACK etc. */ - int crcauto; /* CRC auto mode */ - -#define CRC_MODE_NONE 0 -#define CRC_MODE_FLEX 1 -#define CRC_MODE_SMACK 2 -#define CRC_MODE_FLEX_TEST 3 -#define CRC_MODE_SMACK_TEST 4 - - refcount_t refcnt; - struct completion dead; -}; - -/*---------------------------------------------------------------------------*/ - -static const unsigned short crc_flex_table[] = { - 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, - 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, - 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, - 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, - 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, - 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, - 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, - 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, - 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, - 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, - 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, - 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, - 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, - 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, - 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, - 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, - 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, - 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, - 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, - 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, - 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, - 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, - 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, - 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, - 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, - 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, - 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, - 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, - 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, - 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, - 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, - 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff -}; - -static unsigned short calc_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - while (size--) - crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - return crc; -} - -static int check_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - if (size < 3) - return -1; - - while (size--) - crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - if ((crc & 0xffff) != 0x7070) - return -1; - - return 0; -} - -static int check_crc_16(unsigned char *cp, int size) -{ - unsigned short crc = 0x0000; - - if (size < 3) - return -1; - - crc = crc16(0, cp, size); - - if (crc != 0x0000) - return -1; - - return 0; -} - -/* - * Standard encapsulation - */ - -static int kiss_esc(unsigned char *s, unsigned char *d, int len) -{ - unsigned char *ptr = d; - unsigned char c; - - /* - * Send an initial END character to flush out any data that may have - * accumulated in the receiver due to line noise. - */ - - *ptr++ = END; - - while (len-- > 0) { - switch (c = *s++) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - - *ptr++ = END; - - return ptr - d; -} - -/* - * MW: - * OK its ugly, but tell me a better solution without copying the - * packet to a temporary buffer :-) - */ -static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, - int len) -{ - unsigned char *ptr = d; - unsigned char c=0; - - *ptr++ = END; - while (len > 0) { - if (len > 2) - c = *s++; - else if (len > 1) - c = crc >> 8; - else - c = crc & 0xff; - - len--; - - switch (c) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - *ptr++ = END; - - return ptr - d; -} - -/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ -static void ax_bump(struct mkiss *ax) -{ - struct sk_buff *skb; - int count; - - spin_lock_bh(&ax->buflock); - if (ax->rbuff[0] > 0x0f) { - if (ax->rbuff[0] & 0x80) { - if (check_crc_16(ax->rbuff, ax->rcount) < 0) { - ax->dev->stats.rx_errors++; - spin_unlock_bh(&ax->buflock); - - return; - } - if (ax->crcmode != CRC_MODE_SMACK && ax->crcauto) { - printk(KERN_INFO - "mkiss: %s: Switching to crc-smack\n", - ax->dev->name); - ax->crcmode = CRC_MODE_SMACK; - } - ax->rcount -= 2; - *ax->rbuff &= ~0x80; - } else if (ax->rbuff[0] & 0x20) { - if (check_crc_flex(ax->rbuff, ax->rcount) < 0) { - ax->dev->stats.rx_errors++; - spin_unlock_bh(&ax->buflock); - return; - } - if (ax->crcmode != CRC_MODE_FLEX && ax->crcauto) { - printk(KERN_INFO - "mkiss: %s: Switching to crc-flexnet\n", - ax->dev->name); - ax->crcmode = CRC_MODE_FLEX; - } - ax->rcount -= 2; - - /* - * dl9sau bugfix: the trailling two bytes flexnet crc - * will not be passed to the kernel. thus we have to - * correct the kissparm signature, because it indicates - * a crc but there's none - */ - *ax->rbuff &= ~0x20; - } - } - - count = ax->rcount; - - if ((skb = dev_alloc_skb(count)) == NULL) { - printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", - ax->dev->name); - ax->dev->stats.rx_dropped++; - spin_unlock_bh(&ax->buflock); - return; - } - - skb_put_data(skb, ax->rbuff, count); - skb->protocol = ax25_type_trans(skb, ax->dev); - netif_rx(skb); - ax->dev->stats.rx_packets++; - ax->dev->stats.rx_bytes += count; - spin_unlock_bh(&ax->buflock); -} - -static void kiss_unesc(struct mkiss *ax, unsigned char s) -{ - switch (s) { - case END: - /* drop keeptest bit = VSV */ - if (test_bit(AXF_KEEPTEST, &ax->flags)) - clear_bit(AXF_KEEPTEST, &ax->flags); - - if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2)) - ax_bump(ax); - - clear_bit(AXF_ESCAPE, &ax->flags); - ax->rcount = 0; - return; - - case ESC: - set_bit(AXF_ESCAPE, &ax->flags); - return; - case ESC_ESC: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = ESC; - break; - case ESC_END: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = END; - break; - } - - spin_lock_bh(&ax->buflock); - if (!test_bit(AXF_ERROR, &ax->flags)) { - if (ax->rcount < ax->buffsize) { - ax->rbuff[ax->rcount++] = s; - spin_unlock_bh(&ax->buflock); - return; - } - - ax->dev->stats.rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } - spin_unlock_bh(&ax->buflock); -} - -static int ax_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr_ax25 *sa = addr; - - netif_tx_lock_bh(dev); - netif_addr_lock(dev); - __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN); - netif_addr_unlock(dev); - netif_tx_unlock_bh(dev); - - return 0; -} - -/*---------------------------------------------------------------------------*/ - -static void ax_changedmtu(struct mkiss *ax) -{ - struct net_device *dev = ax->dev; - unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; - int len; - - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - xbuff = kmalloc(len + 4, GFP_ATOMIC); - rbuff = kmalloc(len + 4, GFP_ATOMIC); - - if (xbuff == NULL || rbuff == NULL) { - printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, " - "MTU change cancelled.\n", - ax->dev->name); - dev->mtu = ax->mtu; - kfree(xbuff); - kfree(rbuff); - return; - } - - spin_lock_bh(&ax->buflock); - - oxbuff = ax->xbuff; - ax->xbuff = xbuff; - orbuff = ax->rbuff; - ax->rbuff = rbuff; - - if (ax->xleft) { - if (ax->xleft <= len) { - memcpy(ax->xbuff, ax->xhead, ax->xleft); - } else { - ax->xleft = 0; - dev->stats.tx_dropped++; - } - } - - ax->xhead = ax->xbuff; - - if (ax->rcount) { - if (ax->rcount <= len) { - memcpy(ax->rbuff, orbuff, ax->rcount); - } else { - ax->rcount = 0; - dev->stats.rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } - } - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - - spin_unlock_bh(&ax->buflock); - - kfree(oxbuff); - kfree(orbuff); -} - -/* Encapsulate one AX.25 packet and stuff into a TTY queue. */ -static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) -{ - struct mkiss *ax = netdev_priv(dev); - unsigned char *p; - int actual, count; - - if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */ - ax_changedmtu(ax); - - if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */ - printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name); - dev->stats.tx_dropped++; - netif_start_queue(dev); - return; - } - - p = icp; - - spin_lock_bh(&ax->buflock); - if ((*p & 0x0f) != 0) { - /* Configuration Command (kissparms(1). - * Protocol spec says: never append CRC. - * This fixes a very old bug in the linux - * kiss driver. -- dl9sau */ - switch (*p & 0xff) { - case 0x85: - /* command from userspace especially for us, - * not for delivery to the tnc */ - if (len > 1) { - int cmd = (p[1] & 0xff); - switch(cmd) { - case 3: - ax->crcmode = CRC_MODE_SMACK; - break; - case 2: - ax->crcmode = CRC_MODE_FLEX; - break; - case 1: - ax->crcmode = CRC_MODE_NONE; - break; - case 0: - default: - ax->crcmode = CRC_MODE_SMACK_TEST; - cmd = 0; - } - ax->crcauto = (cmd ? 0 : 1); - printk(KERN_INFO "mkiss: %s: crc mode set to %d\n", - ax->dev->name, cmd); - } - spin_unlock_bh(&ax->buflock); - netif_start_queue(dev); - - return; - default: - count = kiss_esc(p, ax->xbuff, len); - } - } else { - unsigned short crc; - switch (ax->crcmode) { - case CRC_MODE_SMACK_TEST: - ax->crcmode = CRC_MODE_FLEX_TEST; - printk(KERN_INFO "mkiss: %s: Trying crc-smack\n", ax->dev->name); - fallthrough; - case CRC_MODE_SMACK: - *p |= 0x80; - crc = swab16(crc16(0, p, len)); - count = kiss_esc_crc(p, ax->xbuff, crc, len+2); - break; - case CRC_MODE_FLEX_TEST: - ax->crcmode = CRC_MODE_NONE; - printk(KERN_INFO "mkiss: %s: Trying crc-flexnet\n", ax->dev->name); - fallthrough; - case CRC_MODE_FLEX: - *p |= 0x20; - crc = calc_crc_flex(p, len); - count = kiss_esc_crc(p, ax->xbuff, crc, len+2); - break; - - default: - count = kiss_esc(p, ax->xbuff, len); - } - } - spin_unlock_bh(&ax->buflock); - - set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - actual = ax->tty->ops->write(ax->tty, ax->xbuff, count); - dev->stats.tx_packets++; - dev->stats.tx_bytes += actual; - - netif_trans_update(ax->dev); - ax->xleft = count - actual; - ax->xhead = ax->xbuff + actual; -} - -/* Encapsulate an AX.25 packet and kick it into a TTY queue. */ -static netdev_tx_t ax_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (!netif_running(dev)) { - printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); - return NETDEV_TX_BUSY; - } - - if (netif_queue_stopped(dev)) { - /* - * May be we must check transmitter timeout here ? - * 14 Oct 1994 Dmitry Gorodchanin. - */ - if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) { - /* 20 sec timeout not reached */ - return NETDEV_TX_BUSY; - } - - printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, - (tty_chars_in_buffer(ax->tty) || ax->xleft) ? - "bad line quality" : "driver error"); - - ax->xleft = 0; - clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - netif_start_queue(dev); - } - - /* We were not busy, so we are now... :-) */ - netif_stop_queue(dev); - ax_encaps(dev, skb->data, skb->len); - kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static int ax_open_dev(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (ax->tty == NULL) - return -ENODEV; - - return 0; -} - -/* Open the low-level part of the AX25 channel. Easy! */ -static int ax_open(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - unsigned long len; - - if (ax->tty == NULL) - return -ENODEV; - - /* - * Allocate the frame buffers: - * - * rbuff Receive buffer. - * xbuff Transmit buffer. - */ - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto norbuff; - - if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto noxbuff; - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - ax->rcount = 0; - ax->xleft = 0; - - ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */ - - spin_lock_init(&ax->buflock); - - return 0; - -noxbuff: - kfree(ax->rbuff); - -norbuff: - return -ENOMEM; -} - - -/* Close the low-level part of the AX25 channel. Easy! */ -static int ax_close(struct net_device *dev) -{ - struct mkiss *ax = netdev_priv(dev); - - if (ax->tty) - clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); - - netif_stop_queue(dev); - - return 0; -} - -static const struct net_device_ops ax_netdev_ops = { - .ndo_open = ax_open_dev, - .ndo_stop = ax_close, - .ndo_start_xmit = ax_xmit, - .ndo_set_mac_address = ax_set_mac_address, -}; - -static void ax_setup(struct net_device *dev) -{ - /* Finish setting up the DEVICE info. */ - dev->mtu = AX_MTU; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->addr_len = AX25_ADDR_LEN; - dev->type = ARPHRD_AX25; - dev->tx_queue_len = 10; - dev->header_ops = &ax25_header_ops; - dev->netdev_ops = &ax_netdev_ops; - - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); - - dev->flags = IFF_BROADCAST | IFF_MULTICAST; -} - -/* - * We have a potential race on dereferencing tty->disc_data, because the tty - * layer provides no locking at all - thus one cpu could be running - * sixpack_receive_buf while another calls sixpack_close, which zeroes - * tty->disc_data and frees the memory that sixpack_receive_buf is using. The - * best way to fix this is to use a rwlock in the tty struct, but for now we - * use a single global rwlock for all ttys in ppp line discipline. - */ -static DEFINE_RWLOCK(disc_data_lock); - -static struct mkiss *mkiss_get(struct tty_struct *tty) -{ - struct mkiss *ax; - - read_lock(&disc_data_lock); - ax = tty->disc_data; - if (ax) - refcount_inc(&ax->refcnt); - read_unlock(&disc_data_lock); - - return ax; -} - -static void mkiss_put(struct mkiss *ax) -{ - if (refcount_dec_and_test(&ax->refcnt)) - complete(&ax->dead); -} - -static int crc_force = 0; /* Can be overridden with insmod */ - -static int mkiss_open(struct tty_struct *tty) -{ - struct net_device *dev; - struct mkiss *ax; - int err; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (tty->ops->write == NULL) - return -EOPNOTSUPP; - - dev = alloc_netdev(sizeof(struct mkiss), "ax%d", NET_NAME_UNKNOWN, - ax_setup); - if (!dev) { - err = -ENOMEM; - goto out; - } - - ax = netdev_priv(dev); - ax->dev = dev; - - spin_lock_init(&ax->buflock); - refcount_set(&ax->refcnt, 1); - init_completion(&ax->dead); - - ax->tty = tty; - tty->disc_data = ax; - tty->receive_room = 65535; - - tty_driver_flush_buffer(tty); - - /* Restore default settings */ - dev->type = ARPHRD_AX25; - - /* Perform the low-level AX25 initialization. */ - err = ax_open(ax->dev); - if (err) - goto out_free_netdev; - - err = register_netdev(dev); - if (err) - goto out_free_buffers; - - /* after register_netdev() - because else printk smashes the kernel */ - switch (crc_force) { - case 3: - ax->crcmode = CRC_MODE_SMACK; - printk(KERN_INFO "mkiss: %s: crc mode smack forced.\n", - ax->dev->name); - break; - case 2: - ax->crcmode = CRC_MODE_FLEX; - printk(KERN_INFO "mkiss: %s: crc mode flexnet forced.\n", - ax->dev->name); - break; - case 1: - ax->crcmode = CRC_MODE_NONE; - printk(KERN_INFO "mkiss: %s: crc mode disabled.\n", - ax->dev->name); - break; - case 0: - default: - crc_force = 0; - printk(KERN_INFO "mkiss: %s: crc mode is auto.\n", - ax->dev->name); - ax->crcmode = CRC_MODE_SMACK_TEST; - } - ax->crcauto = (crc_force ? 0 : 1); - - netif_start_queue(dev); - - /* Done. We have linked the TTY line to a channel. */ - return 0; - -out_free_buffers: - kfree(ax->rbuff); - kfree(ax->xbuff); - -out_free_netdev: - free_netdev(dev); - -out: - return err; -} - -static void mkiss_close(struct tty_struct *tty) -{ - struct mkiss *ax; - - write_lock_irq(&disc_data_lock); - ax = tty->disc_data; - tty->disc_data = NULL; - write_unlock_irq(&disc_data_lock); - - if (!ax) - return; - - /* - * We have now ensured that nobody can start using ap from now on, but - * we have to wait for all existing users to finish. - */ - if (!refcount_dec_and_test(&ax->refcnt)) - wait_for_completion(&ax->dead); - /* - * Halt the transmit queue so that a new transmit cannot scribble - * on our buffers - */ - netif_stop_queue(ax->dev); - - unregister_netdev(ax->dev); - - /* Free all AX25 frame buffers after unreg. */ - kfree(ax->rbuff); - kfree(ax->xbuff); - - ax->tty = NULL; - - free_netdev(ax->dev); -} - -/* Perform I/O control on an active ax25 channel. */ -static int mkiss_ioctl(struct tty_struct *tty, unsigned int cmd, - unsigned long arg) -{ - struct mkiss *ax = mkiss_get(tty); - struct net_device *dev; - unsigned int tmp, err; - - /* First make sure we're connected. */ - if (ax == NULL) - return -ENXIO; - dev = ax->dev; - - switch (cmd) { - case SIOCGIFNAME: - err = copy_to_user((void __user *) arg, ax->dev->name, - strlen(ax->dev->name) + 1) ? -EFAULT : 0; - break; - - case SIOCGIFENCAP: - err = put_user(4, (int __user *) arg); - break; - - case SIOCSIFENCAP: - if (get_user(tmp, (int __user *) arg)) { - err = -EFAULT; - break; - } - - ax->mode = tmp; - dev->addr_len = AX25_ADDR_LEN; - dev->hard_header_len = AX25_KISS_HEADER_LEN + - AX25_MAX_HEADER_LEN + 3; - dev->type = ARPHRD_AX25; - - err = 0; - break; - - case SIOCSIFHWADDR: { - char addr[AX25_ADDR_LEN]; - - if (copy_from_user(&addr, - (void __user *) arg, AX25_ADDR_LEN)) { - err = -EFAULT; - break; - } - - netif_tx_lock_bh(dev); - __dev_addr_set(dev, addr, AX25_ADDR_LEN); - netif_tx_unlock_bh(dev); - - err = 0; - break; - } - default: - err = -ENOIOCTLCMD; - } - - mkiss_put(ax); - - return err; -} - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the 'tty_io' module in the kernel when - * a block of data has been received, which can now be decapsulated - * and sent on to the AX.25 layer for further processing. - */ -static void mkiss_receive_buf(struct tty_struct *tty, const u8 *cp, - const u8 *fp, size_t count) -{ - struct mkiss *ax = mkiss_get(tty); - - if (!ax) - return; - - /* - * Argh! mtu change time! - costs us the packet part received - * at the change - */ - if (ax->mtu != ax->dev->mtu + 73) - ax_changedmtu(ax); - - /* Read the characters out of the buffer */ - while (count--) { - if (fp != NULL && *fp++) { - if (!test_and_set_bit(AXF_ERROR, &ax->flags)) - ax->dev->stats.rx_errors++; - cp++; - continue; - } - - kiss_unesc(ax, *cp++); - } - - mkiss_put(ax); - tty_unthrottle(tty); -} - -/* - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void mkiss_write_wakeup(struct tty_struct *tty) -{ - struct mkiss *ax = mkiss_get(tty); - int actual; - - if (!ax) - return; - - if (ax->xleft <= 0) { - /* Now serial buffer is almost free & we can start - * transmission of another packet - */ - clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); - - netif_wake_queue(ax->dev); - goto out; - } - - actual = tty->ops->write(tty, ax->xhead, ax->xleft); - ax->xleft -= actual; - ax->xhead += actual; - -out: - mkiss_put(ax); -} - -static struct tty_ldisc_ops ax_ldisc = { - .owner = THIS_MODULE, - .num = N_AX25, - .name = "mkiss", - .open = mkiss_open, - .close = mkiss_close, - .ioctl = mkiss_ioctl, - .receive_buf = mkiss_receive_buf, - .write_wakeup = mkiss_write_wakeup -}; - -static const char banner[] __initconst = KERN_INFO \ - "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n"; -static const char msg_regfail[] __initconst = KERN_ERR \ - "mkiss: can't register line discipline (err = %d)\n"; - -static int __init mkiss_init_driver(void) -{ - int status; - - printk(banner); - - status = tty_register_ldisc(&ax_ldisc); - if (status != 0) - printk(msg_regfail, status); - - return status; -} - -static void __exit mkiss_exit_driver(void) -{ - tty_unregister_ldisc(&ax_ldisc); -} - -MODULE_AUTHOR("Ralf Baechle DL5RB <ralf@linux-mips.org>"); -MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); -module_param(crc_force, int, 0); -MODULE_PARM_DESC(crc_force, "crc [0 = auto | 1 = none | 2 = flexnet | 3 = smack]"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_LDISC(N_AX25); - -module_init(mkiss_init_driver); -module_exit(mkiss_exit_driver); diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c deleted file mode 100644 index 8569db4a7140..000000000000 --- a/drivers/net/hamradio/scc.c +++ /dev/null @@ -1,2179 +0,0 @@ -#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $" - -#define VERSION "3.0" - -/* - * Please use z8530drv-utils-3.0 with this version. - * ------------------ - * - * You can find a subset of the documentation in - * Documentation/networking/device_drivers/hamradio/z8530drv.rst. - */ - -/* - ******************************************************************** - * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 * - ******************************************************************** - - - ******************************************************************** - - Copyright (c) 1993, 2000 Joerg Reuter DL1BKE - - portions (c) 1993 Guido ten Dolle PE1NNZ - - ******************************************************************** - - The driver and the programs in the archive are UNDER CONSTRUCTION. - The code is likely to fail, and so your kernel could --- even - a whole network. - - This driver is intended for Amateur Radio use. If you are running it - for commercial purposes, please drop me a note. I am nosy... - - ...BUT: - - ! You m u s t recognize the appropriate legislations of your country ! - ! before you connect a radio to the SCC board and start to transmit or ! - ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! ! - - For non-Amateur-Radio use please note that you might need a special - allowance/licence from the designer of the SCC Board and/or the - MODEM. - - This program is free software; you can redistribute it and/or modify - it under the terms of the (modified) GNU General Public License - delivered with the Linux kernel source. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should find a copy of the GNU General Public License in - /usr/src/linux/COPYING; - - ******************************************************************** - - - Incomplete history of z8530drv: - ------------------------------- - - 1994-09-13 started to write the driver, rescued most of my own - code (and Hans Alblas' memory buffer pool concept) from - an earlier project "sccdrv" which was initiated by - Guido ten Dolle. Not much of the old driver survived, - though. The first version I put my hands on was sccdrv1.3 - from August 1993. The memory buffer pool concept - appeared in an unauthorized sccdrv version (1.5) from - August 1994. - - 1995-01-31 changed copyright notice to GPL without limitations. - - . - . <SNIP> - . - - 1996-10-05 New semester, new driver... - - * KISS TNC emulator removed (TTY driver) - * Source moved to drivers/net/ - * Includes Z8530 defines from drivers/net/z8530.h - * Uses sk_buffer memory management - * Reduced overhead of /proc/net/z8530drv output - * Streamlined quite a lot things - * Invents brand new bugs... ;-) - - The move to version number 3.0 reflects theses changes. - You can use 'kissbridge' if you need a KISS TNC emulator. - - 1996-12-13 Fixed for Linux networking changes. (G4KLX) - 1997-01-08 Fixed the remaining problems. - 1997-04-02 Hopefully fixed the problems with the new *_timer() - routines, added calibration code. - 1997-10-12 Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO - 1998-01-29 Small fix to avoid lock-up on initialization - 1998-09-29 Fixed the "grouping" bugs, tx_inhibit works again, - using dev->tx_queue_len now instead of MAXQUEUE now. - 1998-10-21 Postponed the spinlock changes, would need a lot of - testing I currently don't have the time to. Softdcd doesn't - work. - 1998-11-04 Softdcd does not work correctly in DPLL mode, in fact it - never did. The DPLL locks on noise, the SYNC unit sees - flags that aren't... Restarting the DPLL does not help - either, it resynchronizes too slow and the first received - frame gets lost. - 2000-02-13 Fixed for new network driver interface changes, still - does TX timeouts itself since it uses its own queue - scheme. - - Thanks to all who contributed to this driver with ideas and bug - reports! - - NB -- if you find errors, change something, please let me know - first before you distribute it... And please don't touch - the version number. Just replace my callsign in - "v3.0.dl1bke" with your own. Just to avoid confusion... - - If you want to add your modification to the linux distribution - please (!) contact me first. - - New versions of the driver will be announced on the linux-hams - mailing list on vger.kernel.org. To subscribe send an e-mail - to majordomo@vger.kernel.org with the following line in - the body of the mail: - - subscribe linux-hams - - The content of the "Subject" field will be ignored. - - vy 73, - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU - Internet: jreuter@yaina.de - www : http://yaina.de/jreuter -*/ - -/* ----------------------------------------------------------------------- */ - -#undef SCC_LDELAY /* slow it even a bit more down */ -#undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */ - -#define SCC_MAXCHIPS 4 /* number of max. supported chips */ -#define SCC_BUFSIZE 384 /* must not exceed 4096 */ -#undef SCC_DEBUG - -#define SCC_DEFAULT_CLOCK 4915200 - /* default pclock if nothing is specified */ - -/* ----------------------------------------------------------------------- */ - -#include <linux/compat.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/string.h> -#include <linux/in.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/delay.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/rtnetlink.h> -#include <linux/if_ether.h> -#include <linux/if_arp.h> -#include <linux/socket.h> -#include <linux/init.h> -#include <linux/scc.h> -#include <linux/ctype.h> -#include <linux/kernel.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/bitops.h> - -#include <net/net_namespace.h> -#include <net/ax25.h> - -#include <asm/irq.h> -#include <asm/io.h> -#include <linux/uaccess.h> - -#include "z8530.h" - -static const char banner[] __initconst = KERN_INFO \ - "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n"; - -static void t_dwait(struct timer_list *t); -static void t_txdelay(struct timer_list *t); -static void t_tail(struct timer_list *t); -static void t_busy(struct timer_list *); -static void t_maxkeyup(struct timer_list *); -static void t_idle(struct timer_list *t); -static void scc_tx_done(struct scc_channel *); -static void scc_start_tx_timer(struct scc_channel *, - void (*)(struct timer_list *), unsigned long); -static void scc_start_maxkeyup(struct scc_channel *); -static void scc_start_defer(struct scc_channel *); - -static void z8530_init(void); - -static void init_channel(struct scc_channel *scc); -static void scc_key_trx (struct scc_channel *scc, char tx); -static void scc_init_timer(struct scc_channel *scc); - -static int scc_net_alloc(const char *name, struct scc_channel *scc); -static void scc_net_setup(struct net_device *dev); -static int scc_net_open(struct net_device *dev); -static int scc_net_close(struct net_device *dev); -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); -static netdev_tx_t scc_net_tx(struct sk_buff *skb, - struct net_device *dev); -static int scc_net_siocdevprivate(struct net_device *dev, struct ifreq *ifr, - void __user *data, int cmd); -static int scc_net_set_mac_address(struct net_device *dev, void *addr); -static struct net_device_stats * scc_net_get_stats(struct net_device *dev); - -static unsigned char SCC_DriverName[] = "scc"; - -static struct irqflags { unsigned char used : 1; } Ivec[NR_IRQS]; - -static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */ - -static struct scc_ctrl { - io_port chan_A; - io_port chan_B; - int irq; -} SCC_ctrl[SCC_MAXCHIPS+1]; - -static unsigned char Driver_Initialized; -static int Nchips; -static io_port Vector_Latch; - - -/* ******************************************************************** */ -/* * Port Access Functions * */ -/* ******************************************************************** */ - -/* These provide interrupt save 2-step access to the Z8530 registers */ - -static DEFINE_SPINLOCK(iolock); /* Guards paired accesses */ - -static inline unsigned char InReg(io_port port, unsigned char reg) -{ - unsigned long flags; - unsigned char r; - - spin_lock_irqsave(&iolock, flags); -#ifdef SCC_LDELAY - Outb(port, reg); - udelay(SCC_LDELAY); - r=Inb(port); - udelay(SCC_LDELAY); -#else - Outb(port, reg); - r=Inb(port); -#endif - spin_unlock_irqrestore(&iolock, flags); - return r; -} - -static inline void OutReg(io_port port, unsigned char reg, unsigned char val) -{ - unsigned long flags; - - spin_lock_irqsave(&iolock, flags); -#ifdef SCC_LDELAY - Outb(port, reg); udelay(SCC_LDELAY); - Outb(port, val); udelay(SCC_LDELAY); -#else - Outb(port, reg); - Outb(port, val); -#endif - spin_unlock_irqrestore(&iolock, flags); -} - -static inline void wr(struct scc_channel *scc, unsigned char reg, - unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); -} - -static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); -} - -static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) -{ - OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); -} - -/* ******************************************************************** */ -/* * Some useful macros * */ -/* ******************************************************************** */ - -static inline void scc_discard_buffers(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - if (scc->tx_buff != NULL) - { - dev_kfree_skb_irq(scc->tx_buff); - scc->tx_buff = NULL; - } - - while (!skb_queue_empty(&scc->tx_queue)) - dev_kfree_skb_irq(skb_dequeue(&scc->tx_queue)); - - spin_unlock_irqrestore(&scc->lock, flags); -} - - - -/* ******************************************************************** */ -/* * Interrupt Service Routines * */ -/* ******************************************************************** */ - - -/* ----> subroutines for the interrupt handlers <---- */ - -static inline void scc_notify(struct scc_channel *scc, int event) -{ - struct sk_buff *skb; - char *bp; - - if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) - return; - - skb = dev_alloc_skb(2); - if (skb != NULL) - { - bp = skb_put(skb, 2); - *bp++ = PARAM_HWEVENT; - *bp++ = event; - scc_net_rx(scc, skb); - } else - scc->stat.nospace++; -} - -static inline void flush_rx_FIFO(struct scc_channel *scc) -{ - int k; - - for (k=0; k<3; k++) - Inb(scc->data); - - if(scc->rx_buff != NULL) /* did we receive something? */ - { - scc->stat.rxerrs++; /* then count it as an error */ - dev_kfree_skb_irq(scc->rx_buff); - scc->rx_buff = NULL; - } -} - -static void start_hunt(struct scc_channel *scc) -{ - if ((scc->modem.clocksrc != CLK_EXTERNAL)) - OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ -} - -/* ----> four different interrupt handlers for Tx, Rx, changing of */ -/* DCD/CTS and Rx/Tx errors */ - -/* Transmitter interrupt handler */ -static inline void scc_txint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.txints++; - skb = scc->tx_buff; - - /* send first octet */ - - if (skb == NULL) - { - skb = skb_dequeue(&scc->tx_queue); - scc->tx_buff = skb; - netif_wake_queue(scc->dev); - - if (skb == NULL) - { - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - if (skb->len == 0) /* Paranoia... */ - { - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - scc->stat.tx_state = TXS_ACTIVE; - - OutReg(scc->ctrl, R0, RES_Tx_CRC); - /* reset CRC generator */ - or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*skb->data); /* send byte */ - skb_pull(skb, 1); - - if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl,RES_EOM_L); - return; - } - - /* End Of Frame... */ - - if (skb->len == 0) - { - Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ - cl(scc, R10, ABUNDER); /* send CRC */ - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ - return; - } - - /* send octet */ - - Outb(scc->data,*skb->data); - skb_pull(skb, 1); -} - - -/* External/Status interrupt handler */ -static inline void scc_exint(struct scc_channel *scc) -{ - unsigned char status,changes,chg_and_stat; - - scc->stat.exints++; - - status = InReg(scc->ctrl,R0); - changes = status ^ scc->status; - chg_and_stat = changes & status; - - /* ABORT: generated whenever DCD drops while receiving */ - - if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ - flush_rx_FIFO(scc); - - /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ - - if ((changes & SYNC_HUNT) && scc->kiss.softdcd) - { - if (status & SYNC_HUNT) - { - scc->dcd = 0; - flush_rx_FIFO(scc); - if ((scc->modem.clocksrc != CLK_EXTERNAL)) - OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - } else { - scc->dcd = 1; - } - - scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON); - } - - /* DCD: on = start to receive packet, off = ABORT condition */ - /* (a successfully received packet generates a special condition int) */ - - if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */ - { - if(status & DCD) /* DCD is now ON */ - { - start_hunt(scc); - scc->dcd = 1; - } else { /* DCD is now OFF */ - cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ - flush_rx_FIFO(scc); - scc->dcd = 0; - } - - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - } - -#ifdef notdef - /* CTS: use external TxDelay (what's that good for?!) - * Anyway: If we _could_ use it (BayCom USCC uses CTS for - * own purposes) we _should_ use the "autoenable" feature - * of the Z8530 and not this interrupt... - */ - - if (chg_and_stat & CTS) /* CTS is now ON */ - { - if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc_start_tx_timer(scc, t_txdelay, 0); - } -#endif - - if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) - { - scc->stat.tx_under++; /* oops, an underrun! count 'em */ - Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ - - if (scc->tx_buff != NULL) - { - dev_kfree_skb_irq(scc->tx_buff); - scc->tx_buff = NULL; - } - - or(scc,R10,ABUNDER); - scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */ - } - - scc->status = status; - Outb(scc->ctrl,RES_EXT_INT); -} - - -/* Receiver interrupt handler */ -static inline void scc_rxint(struct scc_channel *scc) -{ - struct sk_buff *skb; - - scc->stat.rxints++; - - if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Inb(scc->data); /* discard char */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - return; - } - - skb = scc->rx_buff; - - if (skb == NULL) - { - skb = dev_alloc_skb(scc->stat.bufsize); - if (skb == NULL) - { - scc->dev_stat.rx_dropped++; - scc->stat.nospace++; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - scc->rx_buff = skb; - skb_put_u8(skb, 0); /* KISS data */ - } - - if (skb->len >= scc->stat.bufsize) - { -#ifdef notdef - printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); -#endif - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; - } - - skb_put_u8(skb, Inb(scc->data)); -} - - -/* Receive Special Condition interrupt handler */ -static inline void scc_spint(struct scc_channel *scc) -{ - unsigned char status; - struct sk_buff *skb; - - scc->stat.spints++; - - status = InReg(scc->ctrl,R1); /* read receiver status */ - - Inb(scc->data); /* throw away Rx byte */ - skb = scc->rx_buff; - - if(status & Rx_OVR) /* receiver overrun */ - { - scc->stat.rx_over++; /* count them */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - - if (skb != NULL) - dev_kfree_skb_irq(skb); - scc->rx_buff = skb = NULL; - } - - if(status & END_FR && skb != NULL) /* end of frame */ - { - /* CRC okay, frame ends on 8 bit boundary and received something ? */ - - if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) - { - /* ignore last received byte (first of the CRC bytes) */ - skb_trim(skb, skb->len-1); - scc_net_rx(scc, skb); - scc->rx_buff = NULL; - scc->stat.rxframes++; - } else { /* a bad frame */ - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - scc->stat.rxerrs++; - } - } - - Outb(scc->ctrl,ERR_RES); -} - - -/* ----> interrupt service routine for the Z8530 <---- */ - -static void scc_isr_dispatch(struct scc_channel *scc, int vector) -{ - spin_lock(&scc->lock); - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; - } - spin_unlock(&scc->lock); -} - -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. - */ - -#define SCC_IRQTIMEOUT 30000 - -static irqreturn_t scc_isr(int irq, void *dev_id) -{ - int chip_irq = (long) dev_id; - unsigned char vector; - struct scc_channel *scc; - struct scc_ctrl *ctrl; - int k; - - if (Vector_Latch) - { - for(k=0; k < SCC_IRQTIMEOUT; k++) - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - if (vector & 0x01) break; - - scc=&SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - - OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ - } - - if (k == SCC_IRQTIMEOUT) - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); - - return IRQ_HANDLED; - } - - /* Find the SCC generating the interrupt by polling all attached SCCs - * reading RR3A (the interrupt pending register) - */ - - ctrl = SCC_ctrl; - while (ctrl->chan_A) - { - if (ctrl->irq != chip_irq) - { - ctrl++; - continue; - } - - scc = NULL; - for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) - { - vector=InReg(ctrl->chan_B,R2); /* Read the vector */ - if (vector & 0x01) break; - - scc = &SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - } - - if (k == SCC_IRQTIMEOUT) - { - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); - break; - } - - /* This looks weird and it is. At least the BayCom USCC doesn't - * use the Interrupt Daisy Chain, thus we'll have to start - * all over again to be sure not to miss an interrupt from - * (any of) the other chip(s)... - * Honestly, the situation *is* braindamaged... - */ - - if (scc != NULL) - { - OutReg(scc->ctrl,R0,RES_H_IUS); - ctrl = SCC_ctrl; - } else - ctrl++; - } - return IRQ_HANDLED; -} - - - -/* ******************************************************************** */ -/* * Init Channel */ -/* ******************************************************************** */ - - -/* ----> set SCC channel speed <---- */ - -static inline void set_brg(struct scc_channel *scc, unsigned int tc) -{ - cl(scc,R14,BRENABL); /* disable baudrate generator */ - wr(scc,R12,tc & 255); /* brg rate LOW */ - wr(scc,R13,tc >> 8); /* brg rate HIGH */ - or(scc,R14,BRENABL); /* enable baudrate generator */ -} - -static inline void set_speed(struct scc_channel *scc) -{ - unsigned long flags; - spin_lock_irqsave(&scc->lock, flags); - - if (scc->modem.speed > 0) /* paranoia... */ - set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); - - spin_unlock_irqrestore(&scc->lock, flags); -} - - -/* ----> initialize a SCC channel <---- */ - -static inline void init_brg(struct scc_channel *scc) -{ - wr(scc, R14, BRSRC); /* BRG source = PCLK */ - OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ - OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ -} - -/* - * Initialization according to the Z8530 manual (SGS-Thomson's version): - * - * 1. Modes and constants - * - * WR9 11000000 chip reset - * WR4 XXXXXXXX Tx/Rx control, async or sync mode - * WR1 0XX00X00 select W/REQ (optional) - * WR2 XXXXXXXX program interrupt vector - * WR3 XXXXXXX0 select Rx control - * WR5 XXXX0XXX select Tx control - * WR6 XXXXXXXX sync character - * WR7 XXXXXXXX sync character - * WR9 000X0XXX select interrupt control - * WR10 XXXXXXXX miscellaneous control (optional) - * WR11 XXXXXXXX clock control - * WR12 XXXXXXXX time constant lower byte (optional) - * WR13 XXXXXXXX time constant upper byte (optional) - * WR14 XXXXXXX0 miscellaneous control - * WR14 XXXSSSSS commands (optional) - * - * 2. Enables - * - * WR14 000SSSS1 baud rate enable - * WR3 SSSSSSS1 Rx enable - * WR5 SSSS1SSS Tx enable - * WR0 10000000 reset Tx CRG (optional) - * WR1 XSS00S00 DMA enable (optional) - * - * 3. Interrupt status - * - * WR15 XXXXXXXX enable external/status - * WR0 00010000 reset external status - * WR0 00010000 reset external status twice - * WR1 SSSXXSXX enable Rx, Tx and Ext/status - * WR9 000SXSSS enable master interrupt enable - * - * 1 = set to one, 0 = reset to zero - * X = user defined, S = same as previous init - * - * - * Note that the implementation differs in some points from above scheme. - * - */ - -static void init_channel(struct scc_channel *scc) -{ - timer_delete(&scc->tx_t); - timer_delete(&scc->tx_wdog); - - disable_irq(scc->irq); - - wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ - wr(scc,R1,0); /* no W/REQ operation */ - wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */ - wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */ - wr(scc,R6,0); /* SDLC address zero (not used) */ - wr(scc,R7,FLAG); /* SDLC flag value */ - wr(scc,R9,VIS); /* vector includes status */ - wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ - wr(scc,R14, 0); - - -/* set clock sources: - - CLK_DPLL: normal halfduplex operation - - RxClk: use DPLL - TxClk: use DPLL - TRxC mode DPLL output - - CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) - - BayCom: others: - - TxClk = pin RTxC TxClk = pin TRxC - RxClk = pin TRxC RxClk = pin RTxC - - - CLK_DIVIDER: - RxClk = use DPLL - TxClk = pin RTxC - - BayCom: others: - pin TRxC = DPLL pin TRxC = BRG - (RxClk * 1) (RxClk * 32) -*/ - - - switch(scc->modem.clocksrc) - { - case CLK_DPLL: - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - init_brg(scc); - break; - - case CLK_DIVIDER: - wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); - init_brg(scc); - break; - - case CLK_EXTERNAL: - wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); - OutReg(scc->ctrl, R14, DISDPLL); - break; - - } - - set_speed(scc); /* set baudrate */ - - if(scc->enhanced) - { - or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */ - wr(scc,R7,AUTOEOM); - } - - if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD)) - /* DCD is now ON */ - { - start_hunt(scc); - } - - /* enable ABORT, DCD & SYNC/HUNT interrupts */ - - wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE)); - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ - - or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */ - - scc->status = InReg(scc->ctrl,R0); /* read initial status */ - - or(scc,R9,MIE); /* master interrupt enable */ - - scc_init_timer(scc); - - enable_irq(scc->irq); -} - - - - -/* ******************************************************************** */ -/* * SCC timer functions * */ -/* ******************************************************************** */ - - -/* ----> scc_key_trx sets the time constant for the baudrate - generator and keys the transmitter <---- */ - -static void scc_key_trx(struct scc_channel *scc, char tx) -{ - unsigned int time_const; - - if (scc->brand & PRIMUS) - Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); - - if (scc->modem.speed < 300) - scc->modem.speed = 1200; - - time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; - - disable_irq(scc->irq); - - if (tx) - { - or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ - or(scc, R15, TxUIE); - } - - if (scc->modem.clocksrc == CLK_DPLL) - { /* force simplex operation */ - if (tx) - { -#ifdef CONFIG_SCC_TRXECHO - cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ - cl(scc, R15, DCDIE|SYNCIE); /* No DCD changes, please */ -#endif - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ - wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); - - /* By popular demand: tx_inhibit */ - if (scc->kiss.tx_inhibit) - { - or(scc,R5, TxENAB); - scc->wreg[R5] |= RTS; - } else { - or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */ - } - } else { - cl(scc,R5,RTS|TxENAB); - - set_brg(scc, time_const); /* reprogram baudrate generator */ - - /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */ - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - -#ifndef CONFIG_SCC_TRXECHO - if (scc->kiss.softdcd) -#endif - { - or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE); - start_hunt(scc); - } - } - } else { - if (tx) - { -#ifdef CONFIG_SCC_TRXECHO - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - cl(scc, R3, RxENABLE); - cl(scc, R15, DCDIE|SYNCIE); - } -#endif - - if (scc->kiss.tx_inhibit) - { - or(scc,R5, TxENAB); - scc->wreg[R5] |= RTS; - } else { - or(scc,R5,RTS|TxENAB); /* enable tx */ - } - } else { - cl(scc,R5,RTS|TxENAB); /* disable tx */ - - if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) && -#ifndef CONFIG_SCC_TRXECHO - scc->kiss.softdcd) -#else - 1) -#endif - { - or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE); - start_hunt(scc); - } - } - } - - enable_irq(scc->irq); -} - - -/* ----> SCC timer interrupt handler and friends. <---- */ - -static void __scc_start_tx_timer(struct scc_channel *scc, - void (*handler)(struct timer_list *t), - unsigned long when) -{ - timer_delete(&scc->tx_t); - - if (when == 0) - { - handler(&scc->tx_t); - } else - if (when != TIMER_OFF) - { - scc->tx_t.function = handler; - scc->tx_t.expires = jiffies + (when*HZ)/100; - add_timer(&scc->tx_t); - } -} - -static void scc_start_tx_timer(struct scc_channel *scc, - void (*handler)(struct timer_list *t), - unsigned long when) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - __scc_start_tx_timer(scc, handler, when); - spin_unlock_irqrestore(&scc->lock, flags); -} - -static void scc_start_defer(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - - if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) - { - scc->tx_wdog.function = t_busy; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; - add_timer(&scc->tx_wdog); - } - spin_unlock_irqrestore(&scc->lock, flags); -} - -static void scc_start_maxkeyup(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - - if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) - { - scc->tx_wdog.function = t_maxkeyup; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; - add_timer(&scc->tx_wdog); - } - spin_unlock_irqrestore(&scc->lock, flags); -} - -/* - * This is called from scc_txint() when there are no more frames to send. - * Not exactly a timer function, but it is a close friend of the family... - */ - -static void scc_tx_done(struct scc_channel *scc) -{ - /* - * trx remains keyed in fulldup mode 2 until t_idle expires. - */ - - switch (scc->kiss.fulldup) - { - case KISS_DUPLEX_LINK: - scc->stat.tx_state = TXS_IDLE2; - if (scc->kiss.idletime != TIMER_OFF) - scc_start_tx_timer(scc, t_idle, - scc->kiss.idletime*100); - break; - case KISS_DUPLEX_OPTIMA: - scc_notify(scc, HWEV_ALL_SENT); - break; - default: - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - - netif_wake_queue(scc->dev); -} - - -static unsigned char Rand = 17; - -static inline int is_grouped(struct scc_channel *scc) -{ - int k; - struct scc_channel *scc2; - unsigned char grp1, grp2; - - grp1 = scc->kiss.group; - - for (k = 0; k < (Nchips * 2); k++) - { - scc2 = &SCC_Info[k]; - grp2 = scc2->kiss.group; - - if (scc2 == scc || !(scc2->dev && grp2)) - continue; - - if ((grp1 & 0x3f) == (grp2 & 0x3f)) - { - if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) ) - return 1; - - if ( (grp1 & RXGROUP) && scc2->dcd ) - return 1; - } - } - return 0; -} - -/* DWAIT and SLOTTIME expired - * - * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer - * else key trx and start txdelay - * fulldup == 1: key trx and start txdelay - * fulldup == 2: mintime expired, reset status or key trx and start txdelay - */ - -static void t_dwait(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ - { - if (skb_queue_empty(&scc->tx_queue)) { /* nothing to send */ - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); /* t_maxkeyup locked it. */ - return; - } - - scc->stat.tx_state = TXS_BUSY; - } - - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - { - Rand = Rand * 17 + 31; - - if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) - { - scc_start_defer(scc); - scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); - return ; - } - } - - if ( !(scc->wreg[R5] & RTS) ) - { - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } else { - scc_start_tx_timer(scc, t_txdelay, 0); - } -} - - -/* TXDELAY expired - * - * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. - */ - -static void t_txdelay(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - scc_start_maxkeyup(scc); - - if (scc->tx_buff == NULL) - { - disable_irq(scc->irq); - scc_txint(scc); - enable_irq(scc->irq); - } -} - - -/* TAILTIME expired - * - * switch off transmitter. If we were stopped by Maxkeyup restart - * transmission after 'mintime' seconds - */ - -static void t_tail(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - spin_unlock_irqrestore(&scc->lock, flags); - - if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_WAIT; - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - return; - } - - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); -} - - -/* BUSY timeout - * - * throw away send buffers if DCD remains active too long. - */ - -static void t_busy(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - - timer_delete(&scc->tx_t); - netif_stop_queue(scc->dev); /* don't pile on the wabbit! */ - - scc_discard_buffers(scc); - scc->stat.txerrs++; - scc->stat.tx_state = TXS_IDLE; - - netif_wake_queue(scc->dev); -} - -/* MAXKEYUP timeout - * - * this is our watchdog. - */ - -static void t_maxkeyup(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - /* - * let things settle down before we start to - * accept new data. - */ - - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - timer_delete(&scc->tx_t); - - cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ - cl(scc, R15, TxUIE); /* count it. */ - OutReg(scc->ctrl, R0, RES_Tx_P); - - spin_unlock_irqrestore(&scc->lock, flags); - - scc->stat.txerrs++; - scc->stat.tx_state = TXS_TIMEOUT; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); -} - -/* IDLE timeout - * - * in fulldup mode 2 it keys down the transmitter after 'idle' seconds - * of inactivity. We will not restart transmission before 'mintime' - * expires. - */ - -static void t_idle(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_t); - - timer_delete(&scc->tx_wdog); - - scc_key_trx(scc, TX_OFF); - if(scc->kiss.mintime) - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - scc->stat.tx_state = TXS_WAIT; -} - -static void scc_init_timer(struct scc_channel *scc) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - scc->stat.tx_state = TXS_IDLE; - spin_unlock_irqrestore(&scc->lock, flags); -} - - -/* ******************************************************************** */ -/* * Set/get L1 parameters * */ -/* ******************************************************************** */ - - -/* - * this will set the "hardware" parameters through KISS commands or ioctl() - */ - -#define CAST(x) (unsigned long)(x) - -static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) -{ - switch (cmd) - { - case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; - case PARAM_PERSIST: scc->kiss.persist=arg; break; - case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; - case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; - case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_GROUP: scc->kiss.group=arg; break; - case PARAM_IDLE: scc->kiss.idletime=arg; break; - case PARAM_MIN: scc->kiss.mintime=arg; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; - case PARAM_WAIT: scc->kiss.waittime=arg; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; - case PARAM_TX: scc->kiss.tx_inhibit=arg; break; - - case PARAM_SOFTDCD: - scc->kiss.softdcd=arg; - if (arg) - { - or(scc, R15, SYNCIE); - cl(scc, R15, DCDIE); - start_hunt(scc); - } else { - or(scc, R15, DCDIE); - cl(scc, R15, SYNCIE); - } - break; - - case PARAM_SPEED: - if (arg < 256) - scc->modem.speed=arg*100; - else - scc->modem.speed=arg; - - if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ - set_speed(scc); - break; - - case PARAM_RTS: - if ( !(scc->wreg[R5] & RTS) ) - { - if (arg != TX_OFF) { - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } - } else { - if (arg == TX_OFF) - { - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - } - break; - - case PARAM_HWEVENT: - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - break; - - default: return -EINVAL; - } - - return 0; -} - - - -static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) -{ - switch (cmd) - { - case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); - case PARAM_PERSIST: return CAST(scc->kiss.persist); - case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); - case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); - case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); - case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); - case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); - case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); - case PARAM_SPEED: return CAST(scc->modem.speed); - case PARAM_GROUP: return CAST(scc->kiss.group); - case PARAM_IDLE: return CAST(scc->kiss.idletime); - case PARAM_MIN: return CAST(scc->kiss.mintime); - case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); - case PARAM_WAIT: return CAST(scc->kiss.waittime); - case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); - case PARAM_TX: return CAST(scc->kiss.tx_inhibit); - default: return NO_SUCH_PARAM; - } - -} - -#undef CAST - -/* ******************************************************************* */ -/* * Send calibration pattern * */ -/* ******************************************************************* */ - -static void scc_stop_calibrate(struct timer_list *t) -{ - struct scc_channel *scc = timer_container_of(scc, t, tx_wdog); - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - timer_delete(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - wr(scc, R6, 0); - wr(scc, R7, FLAG); - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - netif_wake_queue(scc->dev); - spin_unlock_irqrestore(&scc->lock, flags); -} - - -static void -scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern) -{ - unsigned long flags; - - spin_lock_irqsave(&scc->lock, flags); - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - timer_delete(&scc->tx_wdog); - - scc->tx_wdog.function = scc_stop_calibrate; - scc->tx_wdog.expires = jiffies + HZ*duration; - add_timer(&scc->tx_wdog); - - /* This doesn't seem to work. Why not? */ - wr(scc, R6, 0); - wr(scc, R7, pattern); - - /* - * Don't know if this works. - * Damn, where is my Z8530 programming manual...? - */ - - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - scc_key_trx(scc, TX_ON); - spin_unlock_irqrestore(&scc->lock, flags); -} - -/* ******************************************************************* */ -/* * Init channel structures, special HW, etc... * */ -/* ******************************************************************* */ - -/* - * Reset the Z8530s and setup special hardware - */ - -static void z8530_init(void) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - struct scc_channel *scc; - int chip, k; - unsigned long flags; - char *flag; - - - printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); - - flag=" "; - for (k = 0; k < nr_irqs; k++) - if (Ivec[k].used) - { - printk("%s%d", flag, k); - flag=","; - } - printk("\n"); - - - /* reset and pre-init all chips in the system */ - for (chip = 0; chip < Nchips; chip++) - { - scc=&SCC_Info[2*chip]; - if (!scc->ctrl) continue; - - /* Special SCC cards */ - - if(scc->brand & EAGLE) /* this is an EAGLE card */ - Outb(scc->special,0x08); /* enable interrupt on the board */ - - if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ - Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ - - - /* Reset and pre-init Z8530 */ - - spin_lock_irqsave(&scc->lock, flags); - - Outb(scc->ctrl, 0); - OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ - udelay(100); /* give it 'a bit' more time than required */ - wr(scc, R2, chip*16); /* interrupt vector */ - wr(scc, R9, VIS); /* vector includes status */ - spin_unlock_irqrestore(&scc->lock, flags); - } - - - Driver_Initialized = 1; -} - -/* - * Allocate device structure, err, instance, and register driver - */ - -static int scc_net_alloc(const char *name, struct scc_channel *scc) -{ - int err; - struct net_device *dev; - - dev = alloc_netdev(0, name, NET_NAME_UNKNOWN, scc_net_setup); - if (!dev) - return -ENOMEM; - - dev->ml_priv = scc; - scc->dev = dev; - spin_lock_init(&scc->lock); - timer_setup(&scc->tx_t, NULL, 0); - timer_setup(&scc->tx_wdog, NULL, 0); - - err = register_netdevice(dev); - if (err) { - printk(KERN_ERR "%s: can't register network device (%d)\n", - name, err); - free_netdev(dev); - scc->dev = NULL; - return err; - } - - return 0; -} - - - -/* ******************************************************************** */ -/* * Network driver methods * */ -/* ******************************************************************** */ - -static const struct net_device_ops scc_netdev_ops = { - .ndo_open = scc_net_open, - .ndo_stop = scc_net_close, - .ndo_start_xmit = scc_net_tx, - .ndo_set_mac_address = scc_net_set_mac_address, - .ndo_get_stats = scc_net_get_stats, - .ndo_siocdevprivate = scc_net_siocdevprivate, -}; - -/* ----> Initialize device <----- */ - -static void scc_net_setup(struct net_device *dev) -{ - dev->tx_queue_len = 16; /* should be enough... */ - - dev->netdev_ops = &scc_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->flags = 0; - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -/* ----> open network device <---- */ - -static int scc_net_open(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - - if (!scc->init) - return -EINVAL; - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - - init_channel(scc); - - netif_start_queue(dev); - return 0; -} - -/* ----> close network device <---- */ - -static int scc_net_close(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - unsigned long flags; - - netif_stop_queue(dev); - - spin_lock_irqsave(&scc->lock, flags); - Outb(scc->ctrl,0); /* Make sure pointer is written */ - wr(scc,R1,0); /* disable interrupts */ - wr(scc,R3,0); - spin_unlock_irqrestore(&scc->lock, flags); - - timer_delete_sync(&scc->tx_t); - timer_delete_sync(&scc->tx_wdog); - - scc_discard_buffers(scc); - - return 0; -} - -/* ----> receive frame, called from scc_rxint() <---- */ - -static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) -{ - if (skb->len == 0) { - dev_kfree_skb_irq(skb); - return; - } - - scc->dev_stat.rx_packets++; - scc->dev_stat.rx_bytes += skb->len; - - skb->protocol = ax25_type_trans(skb, scc->dev); - - netif_rx(skb); -} - -/* ----> transmit frame <---- */ - -static netdev_tx_t scc_net_tx(struct sk_buff *skb, struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - unsigned long flags; - char kisscmd; - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - if (skb->len > scc->stat.bufsize || skb->len < 2) { - scc->dev_stat.tx_dropped++; /* bogus frame */ - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - scc->dev_stat.tx_packets++; - scc->dev_stat.tx_bytes += skb->len; - scc->stat.txframes++; - - kisscmd = *skb->data & 0x1f; - skb_pull(skb, 1); - - if (kisscmd) { - scc_set_param(scc, kisscmd, *skb->data); - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - spin_lock_irqsave(&scc->lock, flags); - - if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) { - struct sk_buff *skb_del; - skb_del = skb_dequeue(&scc->tx_queue); - dev_kfree_skb_irq(skb_del); - } - skb_queue_tail(&scc->tx_queue, skb); - netif_trans_update(dev); - - - /* - * Start transmission if the trx state is idle or - * t_idle hasn't expired yet. Use dwait/persistence/slottime - * algorithm for normal halfduplex operation. - */ - - if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) { - scc->stat.tx_state = TXS_BUSY; - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - __scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); - else - __scc_start_tx_timer(scc, t_dwait, 0); - } - spin_unlock_irqrestore(&scc->lock, flags); - return NETDEV_TX_OK; -} - -/* ----> ioctl functions <---- */ - -/* - * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg - * SIOCSCCINI - initialize driver arg: --- - * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg - * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg - * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg - * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg - */ - -static int scc_net_siocdevprivate(struct net_device *dev, - struct ifreq *ifr, void __user *arg, int cmd) -{ - struct scc_kiss_cmd kiss_cmd; - struct scc_mem_config memcfg; - struct scc_hw_config hwcfg; - struct scc_calibrate cal; - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - int chan; - unsigned char device_name[IFNAMSIZ]; - - if (!Driver_Initialized) - { - if (cmd == SIOCSCCCFG) - { - int found = 1; - - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (in_compat_syscall()) - return -EOPNOTSUPP; - - if (!arg) return -EFAULT; - - if (Nchips >= SCC_MAXCHIPS) - return -EINVAL; - - if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) - return -EFAULT; - - if (hwcfg.irq == 2) hwcfg.irq = 9; - - if (hwcfg.irq < 0 || hwcfg.irq >= irq_get_nr_irqs()) - return -EINVAL; - - if (!Ivec[hwcfg.irq].used && hwcfg.irq) - { - if (request_irq(hwcfg.irq, scc_isr, - 0, "AX.25 SCC", - (void *)(long) hwcfg.irq)) - printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); - else - Ivec[hwcfg.irq].used = 1; - } - - if (hwcfg.vector_latch && !Vector_Latch) { - if (!request_region(hwcfg.vector_latch, 1, "scc vector latch")) - printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch); - else - Vector_Latch = hwcfg.vector_latch; - } - - if (hwcfg.clock == 0) - hwcfg.clock = SCC_DEFAULT_CLOCK; - -#ifndef SCC_DONT_CHECK - - if(request_region(hwcfg.ctrl_a, 1, "scc-probe")) - { - disable_irq(hwcfg.irq); - Outb(hwcfg.ctrl_a, 0); - OutReg(hwcfg.ctrl_a, R9, FHWRES); - udelay(100); - OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ - udelay(5); - - if (InReg(hwcfg.ctrl_a,R13) != 0x55) - found = 0; - enable_irq(hwcfg.irq); - release_region(hwcfg.ctrl_a, 1); - } - else - found = 0; -#endif - - if (found) - { - SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; - SCC_Info[2*Nchips ].data = hwcfg.data_a; - SCC_Info[2*Nchips ].irq = hwcfg.irq; - SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; - SCC_Info[2*Nchips+1].data = hwcfg.data_b; - SCC_Info[2*Nchips+1].irq = hwcfg.irq; - - SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; - SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; - SCC_ctrl[Nchips].irq = hwcfg.irq; - } - - - for (chan = 0; chan < 2; chan++) - { - sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); - - SCC_Info[2*Nchips+chan].special = hwcfg.special; - SCC_Info[2*Nchips+chan].clock = hwcfg.clock; - SCC_Info[2*Nchips+chan].brand = hwcfg.brand; - SCC_Info[2*Nchips+chan].option = hwcfg.option; - SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; - -#ifdef SCC_DONT_CHECK - printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", - device_name, - SCC_Info[2*Nchips+chan].data, - SCC_Info[2*Nchips+chan].ctrl); - -#else - printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", - device_name, - chan? hwcfg.data_b : hwcfg.data_a, - chan? hwcfg.ctrl_b : hwcfg.ctrl_a, - found? "found" : "missing"); -#endif - - if (found) - { - request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); - request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); - if (Nchips+chan != 0 && - scc_net_alloc(device_name, - &SCC_Info[2*Nchips+chan])) - return -EINVAL; - } - } - - if (found) Nchips++; - - return 0; - } - - if (cmd == SIOCSCCINI) - { - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - - if (Nchips == 0) - return -EINVAL; - - z8530_init(); - return 0; - } - - return -EINVAL; /* confuse the user */ - } - - if (!scc->init) - { - if (cmd == SIOCSCCCHANINI) - { - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg) return -EINVAL; - - scc->stat.bufsize = SCC_BUFSIZE; - - if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) - return -EINVAL; - - /* default KISS Params */ - - if (scc->modem.speed < 4800) - { - scc->kiss.txdelay = 36; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } else { - scc->kiss.txdelay = 10; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - scc->init = 1; - - return 0; - } - - return -EINVAL; - } - - switch(cmd) - { - case SIOCSCCRESERVED: - return -ENOIOCTLCMD; - - case SIOCSCCSMEM: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) - return -EINVAL; - if (memcfg.bufsize < 16) - return -EINVAL; - scc->stat.bufsize = memcfg.bufsize; - return 0; - - case SIOCSCCGSTAT: - if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) - return -EINVAL; - return 0; - - case SIOCSCCGKISS: - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); - if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) - return -EINVAL; - return 0; - - case SIOCSCCSKISS: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); - - case SIOCSCCCAL: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0) - return -EINVAL; - - scc_start_calibrate(scc, cal.time, cal.pattern); - return 0; - - default: - return -ENOIOCTLCMD; - - } - - return -EINVAL; -} - -/* ----> set interface callsign <---- */ - -static int scc_net_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *) addr; - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* ----> get statistics <---- */ - -static struct net_device_stats *scc_net_get_stats(struct net_device *dev) -{ - struct scc_channel *scc = (struct scc_channel *) dev->ml_priv; - - scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; - scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; - scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; - scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; - - return &scc->dev_stat; -} - -/* ******************************************************************** */ -/* * dump statistics to /proc/net/z8530drv * */ -/* ******************************************************************** */ - -#ifdef CONFIG_PROC_FS - -static inline struct scc_channel *scc_net_seq_idx(loff_t pos) -{ - int k; - - for (k = 0; k < Nchips*2; ++k) { - if (!SCC_Info[k].init) - continue; - if (pos-- == 0) - return &SCC_Info[k]; - } - return NULL; -} - -static void *scc_net_seq_start(struct seq_file *seq, loff_t *pos) -{ - return *pos ? scc_net_seq_idx(*pos - 1) : SEQ_START_TOKEN; - -} - -static void *scc_net_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - unsigned k; - struct scc_channel *scc = v; - ++*pos; - - for (k = (v == SEQ_START_TOKEN) ? 0 : (scc - SCC_Info)+1; - k < Nchips*2; ++k) { - if (SCC_Info[k].init) - return &SCC_Info[k]; - } - return NULL; -} - -static void scc_net_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int scc_net_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) { - seq_puts(seq, "z8530drv-"VERSION"\n"); - } else if (!Driver_Initialized) { - seq_puts(seq, "not initialized\n"); - } else if (!Nchips) { - seq_puts(seq, "chips missing\n"); - } else { - const struct scc_channel *scc = v; - const struct scc_stat *stat = &scc->stat; - const struct scc_kiss *kiss = &scc->kiss; - - - /* dev data ctrl irq clock brand enh vector special option - * baud nrz clocksrc softdcd bufsize - * rxints txints exints spints - * rcvd rxerrs over / xmit txerrs under / nospace bufsize - * txd pers slot tail ful wait min maxk idl defr txof grp - * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## - * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## - */ - - seq_printf(seq, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", - scc->dev->name, - scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, - scc->enhanced, Vector_Latch, scc->special, - scc->option); - seq_printf(seq, "\t%lu %d %d %d %d\n", - scc->modem.speed, scc->modem.nrz, - scc->modem.clocksrc, kiss->softdcd, - stat->bufsize); - seq_printf(seq, "\t%lu %lu %lu %lu\n", - stat->rxints, stat->txints, stat->exints, stat->spints); - seq_printf(seq, "\t%lu %lu %d / %lu %lu %d / %d %d\n", - stat->rxframes, stat->rxerrs, stat->rx_over, - stat->txframes, stat->txerrs, stat->tx_under, - stat->nospace, stat->tx_state); - -#define K(x) kiss->x - seq_printf(seq, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", - K(txdelay), K(persist), K(slottime), K(tailtime), - K(fulldup), K(waittime), K(mintime), K(maxkeyup), - K(idletime), K(maxdefer), K(tx_inhibit), K(group)); -#undef K -#ifdef SCC_DEBUG - { - int reg; - - seq_printf(seq, "\tW "); - for (reg = 0; reg < 16; reg++) - seq_printf(seq, "%2.2x ", scc->wreg[reg]); - seq_printf(seq, "\n"); - - seq_printf(seq, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); - for (reg = 3; reg < 8; reg++) - seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); - seq_printf(seq, "XX "); - for (reg = 9; reg < 16; reg++) - seq_printf(seq, "%2.2x ", InReg(scc->ctrl, reg)); - seq_printf(seq, "\n"); - } -#endif - seq_putc(seq, '\n'); - } - - return 0; -} - -static const struct seq_operations scc_net_seq_ops = { - .start = scc_net_seq_start, - .next = scc_net_seq_next, - .stop = scc_net_seq_stop, - .show = scc_net_seq_show, -}; -#endif /* CONFIG_PROC_FS */ - - -/* ******************************************************************** */ -/* * Init SCC driver * */ -/* ******************************************************************** */ - -static int __init scc_init_driver (void) -{ - char devname[IFNAMSIZ]; - - printk(banner); - - sprintf(devname,"%s0", SCC_DriverName); - - rtnl_lock(); - if (scc_net_alloc(devname, SCC_Info)) { - rtnl_unlock(); - printk(KERN_ERR "z8530drv: cannot initialize module\n"); - return -EIO; - } - rtnl_unlock(); - - proc_create_seq("z8530drv", 0, init_net.proc_net, &scc_net_seq_ops); - - return 0; -} - -static void __exit scc_cleanup_driver(void) -{ - const unsigned int nr_irqs = irq_get_nr_irqs(); - io_port ctrl; - int k; - struct scc_channel *scc; - struct net_device *dev; - - if (Nchips == 0 && (dev = SCC_Info[0].dev)) - { - unregister_netdev(dev); - free_netdev(dev); - } - - /* Guard against chip prattle */ - local_irq_disable(); - - for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k].chan_A) ) - { - Outb(ctrl, 0); - OutReg(ctrl,R9,FHWRES); /* force hardware reset */ - udelay(50); - } - - /* To unload the port must be closed so no real IRQ pending */ - for (k = 0; k < nr_irqs ; k++) - if (Ivec[k].used) free_irq(k, NULL); - - local_irq_enable(); - - /* Now clean up */ - for (k = 0; k < Nchips*2; k++) - { - scc = &SCC_Info[k]; - if (scc->ctrl) - { - release_region(scc->ctrl, 1); - release_region(scc->data, 1); - } - if (scc->dev) - { - unregister_netdev(scc->dev); - free_netdev(scc->dev); - } - } - - - if (Vector_Latch) - release_region(Vector_Latch, 1); - - remove_proc_entry("z8530drv", init_net.proc_net); -} - -MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>"); -MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards"); -MODULE_LICENSE("GPL"); -module_init(scc_init_driver); -module_exit(scc_cleanup_driver); diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c deleted file mode 100644 index 4106f0301ab4..000000000000 --- a/drivers/net/hamradio/yam.c +++ /dev/null @@ -1,1191 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*****************************************************************************/ - -/* - * yam.c -- YAM radio modem driver. - * - * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr) - * Adapted from baycom.c driver written by Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * History: - * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3 - * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration - * 0.2 F6FBB 08.06.98 Added delay after FPGA programming - * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2 - * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistence - * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics - * 0.6 F6FBB 25.08.98 Added 1200Bds format - * 0.7 F6FBB 12.09.98 Added to the kernel configuration - * 0.8 F6FBB 14.10.98 Fixed slottime/persistence timing bug - * OK1ZIA 2.09.01 Fixed "kfree_skb on hard IRQ" - * using dev_kfree_skb_any(). (important in 2.4 kernel) - */ - -/*****************************************************************************/ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/net.h> -#include <linux/in.h> -#include <linux/if.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/bitops.h> -#include <linux/random.h> -#include <asm/io.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/firmware.h> -#include <linux/platform_device.h> - -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <net/ax25.h> - -#include <linux/kernel.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <net/net_namespace.h> - -#include <linux/uaccess.h> -#include <linux/init.h> - -#include <linux/yam.h> - -/* --------------------------------------------------------------------- */ - -static const char yam_drvname[] = "yam"; -static const char yam_drvinfo[] __initconst = KERN_INFO \ - "YAM driver version 0.8 by F1OAT/F6FBB\n"; - -/* --------------------------------------------------------------------- */ - -#define FIRMWARE_9600 "yam/9600.bin" -#define FIRMWARE_1200 "yam/1200.bin" - -#define YAM_9600 1 -#define YAM_1200 2 - -#define NR_PORTS 4 -#define YAM_MAGIC 0xF10A7654 - -/* Transmitter states */ - -#define TX_OFF 0 -#define TX_HEAD 1 -#define TX_DATA 2 -#define TX_CRC1 3 -#define TX_CRC2 4 -#define TX_TAIL 5 - -#define YAM_MAX_FRAME 1024 - -#define DEFAULT_BITRATE 9600 /* bps */ -#define DEFAULT_HOLDD 10 /* sec */ -#define DEFAULT_TXD 300 /* ms */ -#define DEFAULT_TXTAIL 10 /* ms */ -#define DEFAULT_SLOT 100 /* ms */ -#define DEFAULT_PERS 64 /* 0->255 */ - -struct yam_port { - int magic; - int bitrate; - int baudrate; - int iobase; - int irq; - int dupmode; - - struct net_device *dev; - - int nb_rxint; - int nb_mdint; - - /* Parameters section */ - - int txd; /* tx delay */ - int holdd; /* duplex ptt delay */ - int txtail; /* txtail delay */ - int slot; /* slottime */ - int pers; /* persistence */ - - /* Tx section */ - - int tx_state; - int tx_count; - int slotcnt; - unsigned char tx_buf[YAM_MAX_FRAME]; - int tx_len; - int tx_crcl, tx_crch; - struct sk_buff_head send_queue; /* Packets awaiting transmission */ - - /* Rx section */ - - int dcd; - unsigned char rx_buf[YAM_MAX_FRAME]; - int rx_len; - int rx_crcl, rx_crch; -}; - -struct yam_mcs { - unsigned char bits[YAM_FPGA_SIZE]; - int bitrate; - struct yam_mcs *next; -}; - -static struct net_device *yam_devs[NR_PORTS]; - -static struct yam_mcs *yam_data; - -static DEFINE_TIMER(yam_timer, NULL); - -/* --------------------------------------------------------------------- */ - -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define YAM_EXTENT 8 - -/* Interrupt Identification Register Bit Masks */ -#define IIR_NOPEND 1 -#define IIR_MSR 0 -#define IIR_TX 2 -#define IIR_RX 4 -#define IIR_LSR 6 -#define IIR_TIMEOUT 12 /* Fifo mode only */ - -#define IIR_MASK 0x0F - -/* Interrupt Enable Register Bit Masks */ -#define IER_RX 1 /* enable rx interrupt */ -#define IER_TX 2 /* enable tx interrupt */ -#define IER_LSR 4 /* enable line status interrupts */ -#define IER_MSR 8 /* enable modem status interrupts */ - -/* Modem Control Register Bit Masks */ -#define MCR_DTR 0x01 /* DTR output */ -#define MCR_RTS 0x02 /* RTS output */ -#define MCR_OUT1 0x04 /* OUT1 output (not accessible in RS232) */ -#define MCR_OUT2 0x08 /* Master Interrupt enable (must be set on PCs) */ -#define MCR_LOOP 0x10 /* Loopback enable */ - -/* Modem Status Register Bit Masks */ -#define MSR_DCTS 0x01 /* Delta CTS input */ -#define MSR_DDSR 0x02 /* Delta DSR */ -#define MSR_DRIN 0x04 /* Delta RI */ -#define MSR_DDCD 0x08 /* Delta DCD */ -#define MSR_CTS 0x10 /* CTS input */ -#define MSR_DSR 0x20 /* DSR input */ -#define MSR_RING 0x40 /* RI input */ -#define MSR_DCD 0x80 /* DCD input */ - -/* line status register bit mask */ -#define LSR_RXC 0x01 -#define LSR_OE 0x02 -#define LSR_PE 0x04 -#define LSR_FE 0x08 -#define LSR_BREAK 0x10 -#define LSR_THRE 0x20 -#define LSR_TSRE 0x40 - -/* Line Control Register Bit Masks */ -#define LCR_DLAB 0x80 -#define LCR_BREAK 0x40 -#define LCR_PZERO 0x28 -#define LCR_PEVEN 0x18 -#define LCR_PODD 0x08 -#define LCR_STOP1 0x00 -#define LCR_STOP2 0x04 -#define LCR_BIT5 0x00 -#define LCR_BIT6 0x02 -#define LCR_BIT7 0x01 -#define LCR_BIT8 0x03 - -/* YAM Modem <-> UART Port mapping */ - -#define TX_RDY MSR_DCTS /* transmitter ready to send */ -#define RX_DCD MSR_DCD /* carrier detect */ -#define RX_FLAG MSR_RING /* hdlc flag received */ -#define FPGA_DONE MSR_DSR /* FPGA is configured */ -#define PTT_ON (MCR_RTS|MCR_OUT2) /* activate PTT */ -#define PTT_OFF (MCR_DTR|MCR_OUT2) /* release PTT */ - -#define ENABLE_RXINT IER_RX /* enable uart rx interrupt during rx */ -#define ENABLE_TXINT IER_MSR /* enable uart ms interrupt during tx */ -#define ENABLE_RTXINT (IER_RX|IER_MSR) /* full duplex operations */ - - -/************************************************************************* -* CRC Tables -************************************************************************/ - -static const unsigned char chktabl[256] = -{0x00, 0x89, 0x12, 0x9b, 0x24, 0xad, 0x36, 0xbf, 0x48, 0xc1, 0x5a, 0xd3, 0x6c, 0xe5, 0x7e, - 0xf7, 0x81, 0x08, 0x93, 0x1a, 0xa5, 0x2c, 0xb7, 0x3e, 0xc9, 0x40, 0xdb, 0x52, 0xed, 0x64, - 0xff, 0x76, 0x02, 0x8b, 0x10, 0x99, 0x26, 0xaf, 0x34, 0xbd, 0x4a, 0xc3, 0x58, 0xd1, 0x6e, - 0xe7, 0x7c, 0xf5, 0x83, 0x0a, 0x91, 0x18, 0xa7, 0x2e, 0xb5, 0x3c, 0xcb, 0x42, 0xd9, 0x50, - 0xef, 0x66, 0xfd, 0x74, 0x04, 0x8d, 0x16, 0x9f, 0x20, 0xa9, 0x32, 0xbb, 0x4c, 0xc5, 0x5e, - 0xd7, 0x68, 0xe1, 0x7a, 0xf3, 0x85, 0x0c, 0x97, 0x1e, 0xa1, 0x28, 0xb3, 0x3a, 0xcd, 0x44, - 0xdf, 0x56, 0xe9, 0x60, 0xfb, 0x72, 0x06, 0x8f, 0x14, 0x9d, 0x22, 0xab, 0x30, 0xb9, 0x4e, - 0xc7, 0x5c, 0xd5, 0x6a, 0xe3, 0x78, 0xf1, 0x87, 0x0e, 0x95, 0x1c, 0xa3, 0x2a, 0xb1, 0x38, - 0xcf, 0x46, 0xdd, 0x54, 0xeb, 0x62, 0xf9, 0x70, 0x08, 0x81, 0x1a, 0x93, 0x2c, 0xa5, 0x3e, - 0xb7, 0x40, 0xc9, 0x52, 0xdb, 0x64, 0xed, 0x76, 0xff, 0x89, 0x00, 0x9b, 0x12, 0xad, 0x24, - 0xbf, 0x36, 0xc1, 0x48, 0xd3, 0x5a, 0xe5, 0x6c, 0xf7, 0x7e, 0x0a, 0x83, 0x18, 0x91, 0x2e, - 0xa7, 0x3c, 0xb5, 0x42, 0xcb, 0x50, 0xd9, 0x66, 0xef, 0x74, 0xfd, 0x8b, 0x02, 0x99, 0x10, - 0xaf, 0x26, 0xbd, 0x34, 0xc3, 0x4a, 0xd1, 0x58, 0xe7, 0x6e, 0xf5, 0x7c, 0x0c, 0x85, 0x1e, - 0x97, 0x28, 0xa1, 0x3a, 0xb3, 0x44, 0xcd, 0x56, 0xdf, 0x60, 0xe9, 0x72, 0xfb, 0x8d, 0x04, - 0x9f, 0x16, 0xa9, 0x20, 0xbb, 0x32, 0xc5, 0x4c, 0xd7, 0x5e, 0xe1, 0x68, 0xf3, 0x7a, 0x0e, - 0x87, 0x1c, 0x95, 0x2a, 0xa3, 0x38, 0xb1, 0x46, 0xcf, 0x54, 0xdd, 0x62, 0xeb, 0x70, 0xf9, - 0x8f, 0x06, 0x9d, 0x14, 0xab, 0x22, 0xb9, 0x30, 0xc7, 0x4e, 0xd5, 0x5c, 0xe3, 0x6a, 0xf1, - 0x78}; -static const unsigned char chktabh[256] = -{0x00, 0x11, 0x23, 0x32, 0x46, 0x57, 0x65, 0x74, 0x8c, 0x9d, 0xaf, 0xbe, 0xca, 0xdb, 0xe9, - 0xf8, 0x10, 0x01, 0x33, 0x22, 0x56, 0x47, 0x75, 0x64, 0x9c, 0x8d, 0xbf, 0xae, 0xda, 0xcb, - 0xf9, 0xe8, 0x21, 0x30, 0x02, 0x13, 0x67, 0x76, 0x44, 0x55, 0xad, 0xbc, 0x8e, 0x9f, 0xeb, - 0xfa, 0xc8, 0xd9, 0x31, 0x20, 0x12, 0x03, 0x77, 0x66, 0x54, 0x45, 0xbd, 0xac, 0x9e, 0x8f, - 0xfb, 0xea, 0xd8, 0xc9, 0x42, 0x53, 0x61, 0x70, 0x04, 0x15, 0x27, 0x36, 0xce, 0xdf, 0xed, - 0xfc, 0x88, 0x99, 0xab, 0xba, 0x52, 0x43, 0x71, 0x60, 0x14, 0x05, 0x37, 0x26, 0xde, 0xcf, - 0xfd, 0xec, 0x98, 0x89, 0xbb, 0xaa, 0x63, 0x72, 0x40, 0x51, 0x25, 0x34, 0x06, 0x17, 0xef, - 0xfe, 0xcc, 0xdd, 0xa9, 0xb8, 0x8a, 0x9b, 0x73, 0x62, 0x50, 0x41, 0x35, 0x24, 0x16, 0x07, - 0xff, 0xee, 0xdc, 0xcd, 0xb9, 0xa8, 0x9a, 0x8b, 0x84, 0x95, 0xa7, 0xb6, 0xc2, 0xd3, 0xe1, - 0xf0, 0x08, 0x19, 0x2b, 0x3a, 0x4e, 0x5f, 0x6d, 0x7c, 0x94, 0x85, 0xb7, 0xa6, 0xd2, 0xc3, - 0xf1, 0xe0, 0x18, 0x09, 0x3b, 0x2a, 0x5e, 0x4f, 0x7d, 0x6c, 0xa5, 0xb4, 0x86, 0x97, 0xe3, - 0xf2, 0xc0, 0xd1, 0x29, 0x38, 0x0a, 0x1b, 0x6f, 0x7e, 0x4c, 0x5d, 0xb5, 0xa4, 0x96, 0x87, - 0xf3, 0xe2, 0xd0, 0xc1, 0x39, 0x28, 0x1a, 0x0b, 0x7f, 0x6e, 0x5c, 0x4d, 0xc6, 0xd7, 0xe5, - 0xf4, 0x80, 0x91, 0xa3, 0xb2, 0x4a, 0x5b, 0x69, 0x78, 0x0c, 0x1d, 0x2f, 0x3e, 0xd6, 0xc7, - 0xf5, 0xe4, 0x90, 0x81, 0xb3, 0xa2, 0x5a, 0x4b, 0x79, 0x68, 0x1c, 0x0d, 0x3f, 0x2e, 0xe7, - 0xf6, 0xc4, 0xd5, 0xa1, 0xb0, 0x82, 0x93, 0x6b, 0x7a, 0x48, 0x59, 0x2d, 0x3c, 0x0e, 0x1f, - 0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e, - 0x0f}; - -/************************************************************************* -* FPGA functions -************************************************************************/ - -static void delay(int ms) -{ - unsigned long timeout = jiffies + ((ms * HZ) / 1000); - while (time_before(jiffies, timeout)) - cpu_relax(); -} - -/* - * reset FPGA - */ - -static void fpga_reset(int iobase) -{ - outb(0, IER(iobase)); - outb(LCR_DLAB | LCR_BIT5, LCR(iobase)); - outb(1, DLL(iobase)); - outb(0, DLM(iobase)); - - outb(LCR_BIT5, LCR(iobase)); - inb(LSR(iobase)); - inb(MSR(iobase)); - /* turn off FPGA supply voltage */ - outb(MCR_OUT1 | MCR_OUT2, MCR(iobase)); - delay(100); - /* turn on FPGA supply voltage again */ - outb(MCR_DTR | MCR_RTS | MCR_OUT1 | MCR_OUT2, MCR(iobase)); - delay(100); -} - -/* - * send one byte to FPGA - */ - -static int fpga_write(int iobase, unsigned char wrd) -{ - unsigned char bit; - int k; - unsigned long timeout = jiffies + HZ / 10; - - for (k = 0; k < 8; k++) { - bit = (wrd & 0x80) ? (MCR_RTS | MCR_DTR) : MCR_DTR; - outb(bit | MCR_OUT1 | MCR_OUT2, MCR(iobase)); - wrd <<= 1; - outb(0xfc, THR(iobase)); - while ((inb(LSR(iobase)) & LSR_TSRE) == 0) - if (time_after(jiffies, timeout)) - return -1; - } - - return 0; -} - -/* - * predef should be 0 for loading user defined mcs - * predef should be YAM_1200 for loading predef 1200 mcs - * predef should be YAM_9600 for loading predef 9600 mcs - */ -static unsigned char *add_mcs(unsigned char *bits, int bitrate, - unsigned int predef) -{ - const char *fw_name[2] = {FIRMWARE_9600, FIRMWARE_1200}; - const struct firmware *fw; - struct platform_device *pdev; - struct yam_mcs *p; - int err; - - switch (predef) { - case 0: - fw = NULL; - break; - case YAM_1200: - case YAM_9600: - predef--; - pdev = platform_device_register_simple("yam", 0, NULL, 0); - if (IS_ERR(pdev)) { - printk(KERN_ERR "yam: Failed to register firmware\n"); - return NULL; - } - err = request_firmware(&fw, fw_name[predef], &pdev->dev); - platform_device_unregister(pdev); - if (err) { - printk(KERN_ERR "Failed to load firmware \"%s\"\n", - fw_name[predef]); - return NULL; - } - if (fw->size != YAM_FPGA_SIZE) { - printk(KERN_ERR "Bogus length %zu in firmware \"%s\"\n", - fw->size, fw_name[predef]); - release_firmware(fw); - return NULL; - } - bits = (unsigned char *)fw->data; - break; - default: - printk(KERN_ERR "yam: Invalid predef number %u\n", predef); - return NULL; - } - - /* If it already exists, replace the bit data */ - p = yam_data; - while (p) { - if (p->bitrate == bitrate) { - memcpy(p->bits, bits, YAM_FPGA_SIZE); - goto out; - } - p = p->next; - } - - /* Allocate a new mcs */ - if ((p = kmalloc_obj(struct yam_mcs)) == NULL) { - release_firmware(fw); - return NULL; - } - memcpy(p->bits, bits, YAM_FPGA_SIZE); - p->bitrate = bitrate; - p->next = yam_data; - yam_data = p; - out: - release_firmware(fw); - return p->bits; -} - -static unsigned char *get_mcs(int bitrate) -{ - struct yam_mcs *p; - - p = yam_data; - while (p) { - if (p->bitrate == bitrate) - return p->bits; - p = p->next; - } - - /* Load predefined mcs data */ - switch (bitrate) { - case 1200: - /* setting predef as YAM_1200 for loading predef 1200 mcs */ - return add_mcs(NULL, bitrate, YAM_1200); - default: - /* setting predef as YAM_9600 for loading predef 9600 mcs */ - return add_mcs(NULL, bitrate, YAM_9600); - } -} - -/* - * download bitstream to FPGA - * data is contained in bits[] array in yam1200.h resp. yam9600.h - */ - -static int fpga_download(int iobase, int bitrate) -{ - int i, rc; - unsigned char *pbits; - - pbits = get_mcs(bitrate); - if (pbits == NULL) - return -1; - - fpga_reset(iobase); - for (i = 0; i < YAM_FPGA_SIZE; i++) { - if (fpga_write(iobase, pbits[i])) { - printk(KERN_ERR "yam: error in write cycle\n"); - return -1; /* write... */ - } - } - - fpga_write(iobase, 0xFF); - rc = inb(MSR(iobase)); /* check DONE signal */ - - /* Needed for some hardwares */ - delay(50); - - return (rc & MSR_DSR) ? 0 : -1; -} - - -/************************************************************************ -* Serial port init -************************************************************************/ - -static void yam_set_uart(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - int divisor = 115200 / yp->baudrate; - - outb(0, IER(dev->base_addr)); - outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr)); - outb(divisor, DLL(dev->base_addr)); - outb(0, DLM(dev->base_addr)); - outb(LCR_BIT8, LCR(dev->base_addr)); - outb(PTT_OFF, MCR(dev->base_addr)); - outb(0x00, FCR(dev->base_addr)); - - /* Flush pending irq */ - - inb(RBR(dev->base_addr)); - inb(MSR(dev->base_addr)); - - /* Enable rx irq */ - - outb(ENABLE_RTXINT, IER(dev->base_addr)); -} - - -/* --------------------------------------------------------------------- */ - -enum uart { - c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A -}; - -static const char *uart_str[] = -{"unknown", "8250", "16450", "16550", "16550A"}; - -static enum uart yam_check_uart(unsigned int iobase) -{ - unsigned char b1, b2, b3; - enum uart u; - enum uart uart_tab[] = - {c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A}; - - b1 = inb(MCR(iobase)); - outb(b1 | 0x10, MCR(iobase)); /* loopback mode */ - b2 = inb(MSR(iobase)); - outb(0x1a, MCR(iobase)); - b3 = inb(MSR(iobase)) & 0xf0; - outb(b1, MCR(iobase)); /* restore old values */ - outb(b2, MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(RBR(iobase)); - inb(RBR(iobase)); - outb(0x01, FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, SCR(iobase)); - b1 = inb(SCR(iobase)); - outb(0xa5, SCR(iobase)); - b2 = inb(SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/****************************************************************************** -* Rx Section -******************************************************************************/ -static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp) -{ - if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) { - int pkt_len = yp->rx_len - 2 + 1; /* -CRC + kiss */ - struct sk_buff *skb; - - if ((yp->rx_crch & yp->rx_crcl) != 0xFF) { - /* Bad crc */ - } else { - if (!(skb = dev_alloc_skb(pkt_len))) { - printk(KERN_WARNING "%s: memory squeeze, dropping packet\n", dev->name); - ++dev->stats.rx_dropped; - } else { - unsigned char *cp; - cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, yp->rx_buf, pkt_len - 1); - skb->protocol = ax25_type_trans(skb, dev); - netif_rx(skb); - ++dev->stats.rx_packets; - } - } - } - yp->rx_len = 0; - yp->rx_crcl = 0x21; - yp->rx_crch = 0xf3; -} - -static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb) -{ - if (yp->rx_len < YAM_MAX_FRAME) { - unsigned char c = yp->rx_crcl; - yp->rx_crcl = (chktabl[c] ^ yp->rx_crch); - yp->rx_crch = (chktabh[c] ^ rxb); - yp->rx_buf[yp->rx_len++] = rxb; - } -} - -/******************************************************************************** -* TX Section -********************************************************************************/ - -static void ptt_on(struct net_device *dev) -{ - outb(PTT_ON, MCR(dev->base_addr)); -} - -static void ptt_off(struct net_device *dev) -{ - outb(PTT_OFF, MCR(dev->base_addr)); -} - -static netdev_tx_t yam_send_packet(struct sk_buff *skb, - struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - if (skb->protocol == htons(ETH_P_IP)) - return ax25_ip_xmit(skb); - - skb_queue_tail(&yp->send_queue, skb); - netif_trans_update(dev); - return NETDEV_TX_OK; -} - -static void yam_start_tx(struct net_device *dev, struct yam_port *yp) -{ - if ((yp->tx_state == TX_TAIL) || (yp->txd == 0)) - yp->tx_count = 1; - else - yp->tx_count = (yp->bitrate * yp->txd) / 8000; - yp->tx_state = TX_HEAD; - ptt_on(dev); -} - -static void yam_arbitrate(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - if (yp->magic != YAM_MAGIC || yp->tx_state != TX_OFF || - skb_queue_empty(&yp->send_queue)) - return; - /* tx_state is TX_OFF and there is data to send */ - - if (yp->dupmode) { - /* Full duplex mode, don't wait */ - yam_start_tx(dev, yp); - return; - } - if (yp->dcd) { - /* DCD on, wait slotime ... */ - yp->slotcnt = yp->slot / 10; - return; - } - /* Is slottime passed ? */ - if ((--yp->slotcnt) > 0) - return; - - yp->slotcnt = yp->slot / 10; - - /* is random > persist ? */ - if (get_random_u8() > yp->pers) - return; - - yam_start_tx(dev, yp); -} - -static void yam_dotimer(struct timer_list *unused) -{ - int i; - - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev = yam_devs[i]; - if (dev && netif_running(dev)) - yam_arbitrate(dev); - } - yam_timer.expires = jiffies + HZ / 100; - add_timer(&yam_timer); -} - -static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) -{ - struct sk_buff *skb; - unsigned char b, temp; - - switch (yp->tx_state) { - case TX_OFF: - break; - case TX_HEAD: - if (--yp->tx_count <= 0) { - if (!(skb = skb_dequeue(&yp->send_queue))) { - ptt_off(dev); - yp->tx_state = TX_OFF; - break; - } - yp->tx_state = TX_DATA; - if (skb->data[0] != 0) { -/* do_kiss_params(s, skb->data, skb->len); */ - dev_kfree_skb_any(skb); - break; - } - yp->tx_len = skb->len - 1; /* strip KISS byte */ - if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) { - dev_kfree_skb_any(skb); - break; - } - skb_copy_from_linear_data_offset(skb, 1, - yp->tx_buf, - yp->tx_len); - dev_kfree_skb_any(skb); - yp->tx_count = 0; - yp->tx_crcl = 0x21; - yp->tx_crch = 0xf3; - yp->tx_state = TX_DATA; - } - break; - case TX_DATA: - b = yp->tx_buf[yp->tx_count++]; - outb(b, THR(dev->base_addr)); - temp = yp->tx_crcl; - yp->tx_crcl = chktabl[temp] ^ yp->tx_crch; - yp->tx_crch = chktabh[temp] ^ b; - if (yp->tx_count >= yp->tx_len) { - yp->tx_state = TX_CRC1; - } - break; - case TX_CRC1: - yp->tx_crch = chktabl[yp->tx_crcl] ^ yp->tx_crch; - yp->tx_crcl = chktabh[yp->tx_crcl] ^ chktabl[yp->tx_crch] ^ 0xff; - outb(yp->tx_crcl, THR(dev->base_addr)); - yp->tx_state = TX_CRC2; - break; - case TX_CRC2: - outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr)); - if (skb_queue_empty(&yp->send_queue)) { - yp->tx_count = (yp->bitrate * yp->txtail) / 8000; - if (yp->dupmode == 2) - yp->tx_count += (yp->bitrate * yp->holdd) / 8; - if (yp->tx_count == 0) - yp->tx_count = 1; - yp->tx_state = TX_TAIL; - } else { - yp->tx_count = 1; - yp->tx_state = TX_HEAD; - } - ++dev->stats.tx_packets; - break; - case TX_TAIL: - if (--yp->tx_count <= 0) { - yp->tx_state = TX_OFF; - ptt_off(dev); - } - break; - } -} - -/*********************************************************************************** -* ISR routine -************************************************************************************/ - -static irqreturn_t yam_interrupt(int irq, void *dev_id) -{ - struct net_device *dev; - struct yam_port *yp; - unsigned char iir; - int counter = 100; - int i; - int handled = 0; - - for (i = 0; i < NR_PORTS; i++) { - dev = yam_devs[i]; - yp = netdev_priv(dev); - - if (!netif_running(dev)) - continue; - - while ((iir = IIR_MASK & inb(IIR(dev->base_addr))) != IIR_NOPEND) { - unsigned char msr = inb(MSR(dev->base_addr)); - unsigned char lsr = inb(LSR(dev->base_addr)); - unsigned char rxb; - - handled = 1; - - if (lsr & LSR_OE) - ++dev->stats.rx_fifo_errors; - - yp->dcd = (msr & RX_DCD) ? 1 : 0; - - if (--counter <= 0) { - printk(KERN_ERR "%s: too many irq iir=%d\n", - dev->name, iir); - goto out; - } - if (msr & TX_RDY) { - ++yp->nb_mdint; - yam_tx_byte(dev, yp); - } - if (lsr & LSR_RXC) { - ++yp->nb_rxint; - rxb = inb(RBR(dev->base_addr)); - if (msr & RX_FLAG) - yam_rx_flag(dev, yp); - else - yam_rx_byte(dev, yp, rxb); - } - } - } -out: - return IRQ_RETVAL(handled); -} - -#ifdef CONFIG_PROC_FS - -static void *yam_seq_start(struct seq_file *seq, loff_t *pos) -{ - return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL; -} - -static void *yam_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return (*pos < NR_PORTS) ? yam_devs[*pos] : NULL; -} - -static void yam_seq_stop(struct seq_file *seq, void *v) -{ -} - -static int yam_seq_show(struct seq_file *seq, void *v) -{ - struct net_device *dev = v; - const struct yam_port *yp = netdev_priv(dev); - - seq_printf(seq, "Device %s\n", dev->name); - seq_printf(seq, " Up %d\n", netif_running(dev)); - seq_printf(seq, " Speed %u\n", yp->bitrate); - seq_printf(seq, " IoBase 0x%x\n", yp->iobase); - seq_printf(seq, " BaudRate %u\n", yp->baudrate); - seq_printf(seq, " IRQ %u\n", yp->irq); - seq_printf(seq, " TxState %u\n", yp->tx_state); - seq_printf(seq, " Duplex %u\n", yp->dupmode); - seq_printf(seq, " HoldDly %u\n", yp->holdd); - seq_printf(seq, " TxDelay %u\n", yp->txd); - seq_printf(seq, " TxTail %u\n", yp->txtail); - seq_printf(seq, " SlotTime %u\n", yp->slot); - seq_printf(seq, " Persist %u\n", yp->pers); - seq_printf(seq, " TxFrames %lu\n", dev->stats.tx_packets); - seq_printf(seq, " RxFrames %lu\n", dev->stats.rx_packets); - seq_printf(seq, " TxInt %u\n", yp->nb_mdint); - seq_printf(seq, " RxInt %u\n", yp->nb_rxint); - seq_printf(seq, " RxOver %lu\n", dev->stats.rx_fifo_errors); - seq_printf(seq, "\n"); - return 0; -} - -static const struct seq_operations yam_seqops = { - .start = yam_seq_start, - .next = yam_seq_next, - .stop = yam_seq_stop, - .show = yam_seq_show, -}; -#endif - - -/* --------------------------------------------------------------------- */ - -static int yam_open(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - enum uart u; - int i; - int ret=0; - - printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq); - - if (!yp->bitrate) - return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT || - dev->irq < 2 || dev->irq > 15) { - return -ENXIO; - } - if (!request_region(dev->base_addr, YAM_EXTENT, dev->name)) - { - printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr); - return -EACCES; - } - if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) { - printk(KERN_ERR "%s: cannot find uart type\n", dev->name); - ret = -EIO; - goto out_release_base; - } - if (fpga_download(dev->base_addr, yp->bitrate)) { - printk(KERN_ERR "%s: cannot init FPGA\n", dev->name); - ret = -EIO; - goto out_release_base; - } - outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, yam_interrupt, IRQF_SHARED, dev->name, dev)) { - printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq); - ret = -EBUSY; - goto out_release_base; - } - - yam_set_uart(dev); - - netif_start_queue(dev); - - yp->slotcnt = yp->slot / 10; - - /* Reset overruns for all ports - FPGA programming makes overruns */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *yam_dev = yam_devs[i]; - - inb(LSR(yam_dev->base_addr)); - yam_dev->stats.rx_fifo_errors = 0; - } - - printk(KERN_INFO "%s at iobase 0x%lx irq %u uart %s\n", dev->name, dev->base_addr, dev->irq, - uart_str[u]); - return 0; - -out_release_base: - release_region(dev->base_addr, YAM_EXTENT); - return ret; -} - -/* --------------------------------------------------------------------- */ - -static int yam_close(struct net_device *dev) -{ - struct sk_buff *skb; - struct yam_port *yp = netdev_priv(dev); - - if (!dev) - return -EINVAL; - - /* - * disable interrupts - */ - outb(0, IER(dev->base_addr)); - outb(1, MCR(dev->base_addr)); - /* Remove IRQ handler if last */ - free_irq(dev->irq,dev); - release_region(dev->base_addr, YAM_EXTENT); - netif_stop_queue(dev); - while ((skb = skb_dequeue(&yp->send_queue))) - dev_kfree_skb(skb); - - printk(KERN_INFO "%s: close yam at iobase 0x%lx irq %u\n", - yam_drvname, dev->base_addr, dev->irq); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int yam_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data, int cmd) -{ - struct yam_port *yp = netdev_priv(dev); - struct yamdrv_ioctl_cfg yi; - struct yamdrv_ioctl_mcs *ym; - int ioctl_cmd; - - if (copy_from_user(&ioctl_cmd, data, sizeof(int))) - return -EFAULT; - - if (yp->magic != YAM_MAGIC) - return -EINVAL; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - - if (cmd != SIOCDEVPRIVATE) - return -EINVAL; - - switch (ioctl_cmd) { - - case SIOCYAMRESERVED: - return -EINVAL; /* unused */ - - case SIOCYAMSMCS: - if (netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - ym = memdup_user(data, sizeof(struct yamdrv_ioctl_mcs)); - if (IS_ERR(ym)) - return PTR_ERR(ym); - if (ym->cmd != SIOCYAMSMCS || ym->bitrate > YAM_MAXBITRATE) { - kfree(ym); - return -EINVAL; - } - /* setting predef as 0 for loading userdefined mcs data */ - add_mcs(ym->bits, ym->bitrate, 0); - kfree(ym); - break; - - case SIOCYAMSCFG: - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - if (copy_from_user(&yi, data, sizeof(struct yamdrv_ioctl_cfg))) - return -EFAULT; - - if (yi.cmd != SIOCYAMSCFG) - return -EINVAL; - if ((yi.cfg.mask & YAM_IOBASE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_IRQ) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - - if (yi.cfg.mask & YAM_IOBASE) { - yp->iobase = yi.cfg.iobase; - dev->base_addr = yi.cfg.iobase; - } - if (yi.cfg.mask & YAM_IRQ) { - if (yi.cfg.irq > 15) - return -EINVAL; - yp->irq = yi.cfg.irq; - dev->irq = yi.cfg.irq; - } - if (yi.cfg.mask & YAM_BITRATE) { - if (yi.cfg.bitrate > YAM_MAXBITRATE) - return -EINVAL; - yp->bitrate = yi.cfg.bitrate; - } - if (yi.cfg.mask & YAM_BAUDRATE) { - if (yi.cfg.baudrate > YAM_MAXBAUDRATE) - return -EINVAL; - yp->baudrate = yi.cfg.baudrate; - } - if (yi.cfg.mask & YAM_MODE) { - if (yi.cfg.mode > YAM_MAXMODE) - return -EINVAL; - yp->dupmode = yi.cfg.mode; - } - if (yi.cfg.mask & YAM_HOLDDLY) { - if (yi.cfg.holddly > YAM_MAXHOLDDLY) - return -EINVAL; - yp->holdd = yi.cfg.holddly; - } - if (yi.cfg.mask & YAM_TXDELAY) { - if (yi.cfg.txdelay > YAM_MAXTXDELAY) - return -EINVAL; - yp->txd = yi.cfg.txdelay; - } - if (yi.cfg.mask & YAM_TXTAIL) { - if (yi.cfg.txtail > YAM_MAXTXTAIL) - return -EINVAL; - yp->txtail = yi.cfg.txtail; - } - if (yi.cfg.mask & YAM_PERSIST) { - if (yi.cfg.persist > YAM_MAXPERSIST) - return -EINVAL; - yp->pers = yi.cfg.persist; - } - if (yi.cfg.mask & YAM_SLOTTIME) { - if (yi.cfg.slottime > YAM_MAXSLOTTIME) - return -EINVAL; - yp->slot = yi.cfg.slottime; - yp->slotcnt = yp->slot / 10; - } - break; - - case SIOCYAMGCFG: - memset(&yi, 0, sizeof(yi)); - yi.cfg.mask = 0xffffffff; - yi.cfg.iobase = yp->iobase; - yi.cfg.irq = yp->irq; - yi.cfg.bitrate = yp->bitrate; - yi.cfg.baudrate = yp->baudrate; - yi.cfg.mode = yp->dupmode; - yi.cfg.txdelay = yp->txd; - yi.cfg.holddly = yp->holdd; - yi.cfg.txtail = yp->txtail; - yi.cfg.persist = yp->pers; - yi.cfg.slottime = yp->slot; - if (copy_to_user(data, &yi, sizeof(struct yamdrv_ioctl_cfg))) - return -EFAULT; - break; - - default: - return -EINVAL; - - } - - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int yam_set_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = (struct sockaddr *) addr; - - /* addr is an AX.25 shifted ASCII mac address */ - dev_addr_set(dev, sa->sa_data); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static const struct net_device_ops yam_netdev_ops = { - .ndo_open = yam_open, - .ndo_stop = yam_close, - .ndo_start_xmit = yam_send_packet, - .ndo_siocdevprivate = yam_siocdevprivate, - .ndo_set_mac_address = yam_set_mac_address, -}; - -static void yam_setup(struct net_device *dev) -{ - struct yam_port *yp = netdev_priv(dev); - - yp->magic = YAM_MAGIC; - yp->bitrate = DEFAULT_BITRATE; - yp->baudrate = DEFAULT_BITRATE * 2; - yp->iobase = 0; - yp->irq = 0; - yp->dupmode = 0; - yp->holdd = DEFAULT_HOLDD; - yp->txd = DEFAULT_TXD; - yp->txtail = DEFAULT_TXTAIL; - yp->slot = DEFAULT_SLOT; - yp->pers = DEFAULT_PERS; - yp->dev = dev; - - dev->base_addr = yp->iobase; - dev->irq = yp->irq; - - skb_queue_head_init(&yp->send_queue); - - dev->netdev_ops = &yam_netdev_ops; - dev->header_ops = &ax25_header_ops; - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN; - dev->mtu = AX25_MTU; - dev->addr_len = AX25_ADDR_LEN; - memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); - dev_addr_set(dev, (u8 *)&ax25_defaddr); -} - -static int __init yam_init_driver(void) -{ - struct net_device *dev; - int i, err; - char name[IFNAMSIZ]; - - printk(yam_drvinfo); - - for (i = 0; i < NR_PORTS; i++) { - sprintf(name, "yam%d", i); - - dev = alloc_netdev(sizeof(struct yam_port), name, - NET_NAME_UNKNOWN, yam_setup); - if (!dev) { - pr_err("yam: cannot allocate net device\n"); - err = -ENOMEM; - goto error; - } - - err = register_netdev(dev); - if (err) { - printk(KERN_WARNING "yam: cannot register net device %s\n", dev->name); - free_netdev(dev); - goto error; - } - yam_devs[i] = dev; - - } - - timer_setup(&yam_timer, yam_dotimer, 0); - yam_timer.expires = jiffies + HZ / 100; - add_timer(&yam_timer); - - proc_create_seq("yam", 0444, init_net.proc_net, &yam_seqops); - return 0; - error: - while (--i >= 0) { - unregister_netdev(yam_devs[i]); - free_netdev(yam_devs[i]); - } - return err; -} - -/* --------------------------------------------------------------------- */ - -static void __exit yam_cleanup_driver(void) -{ - struct yam_mcs *p; - int i; - - timer_delete_sync(&yam_timer); - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev = yam_devs[i]; - if (dev) { - unregister_netdev(dev); - free_netdev(dev); - } - } - - while (yam_data) { - p = yam_data; - yam_data = yam_data->next; - kfree(p); - } - - remove_proc_entry("yam", init_net.proc_net); -} - -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr"); -MODULE_DESCRIPTION("Yam amateur radio modem driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE(FIRMWARE_1200); -MODULE_FIRMWARE(FIRMWARE_9600); - -module_init(yam_init_driver); -module_exit(yam_cleanup_driver); - -/* --------------------------------------------------------------------- */ - diff --git a/drivers/net/hamradio/z8530.h b/drivers/net/hamradio/z8530.h deleted file mode 100644 index 1655901d713b..000000000000 --- a/drivers/net/hamradio/z8530.h +++ /dev/null @@ -1,246 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -/* 8530 Serial Communications Controller Register definitions */ -#define FLAG 0x7e - -/* Write Register 0 */ -#define R0 0 /* Register selects */ -#define R1 1 -#define R2 2 -#define R3 3 -#define R4 4 -#define R5 5 -#define R6 6 -#define R7 7 -#define R8 8 -#define R9 9 -#define R10 10 -#define R11 11 -#define R12 12 -#define R13 13 -#define R14 14 -#define R15 15 - -#define NULLCODE 0 /* Null Code */ -#define POINT_HIGH 0x8 /* Select upper half of registers */ -#define RES_EXT_INT 0x10 /* Reset Ext. Status Interrupts */ -#define SEND_ABORT 0x18 /* HDLC Abort */ -#define RES_RxINT_FC 0x20 /* Reset RxINT on First Character */ -#define RES_Tx_P 0x28 /* Reset TxINT Pending */ -#define ERR_RES 0x30 /* Error Reset */ -#define RES_H_IUS 0x38 /* Reset highest IUS */ - -#define RES_Rx_CRC 0x40 /* Reset Rx CRC Checker */ -#define RES_Tx_CRC 0x80 /* Reset Tx CRC Checker */ -#define RES_EOM_L 0xC0 /* Reset EOM latch */ - -/* Write Register 1 */ - -#define EXT_INT_ENAB 0x1 /* Ext Int Enable */ -#define TxINT_ENAB 0x2 /* Tx Int Enable */ -#define PAR_SPEC 0x4 /* Parity is special condition */ - -#define RxINT_DISAB 0 /* Rx Int Disable */ -#define RxINT_FCERR 0x8 /* Rx Int on First Character Only or Error */ -#define INT_ALL_Rx 0x10 /* Int on all Rx Characters or error */ -#define INT_ERR_Rx 0x18 /* Int on error only */ - -#define WT_RDY_RT 0x20 /* Wait/Ready on R/T */ -#define WT_FN_RDYFN 0x40 /* Wait/FN/Ready FN */ -#define WT_RDY_ENAB 0x80 /* Wait/Ready Enable */ - -/* Write Register #2 (Interrupt Vector) */ - -/* Write Register 3 */ - -#define RxENABLE 0x1 /* Rx Enable */ -#define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ -#define ADD_SM 0x4 /* Address Search Mode (SDLC) */ -#define RxCRC_ENAB 0x8 /* Rx CRC Enable */ -#define ENT_HM 0x10 /* Enter Hunt Mode */ -#define AUTO_ENAB 0x20 /* Auto Enables */ -#define Rx5 0x0 /* Rx 5 Bits/Character */ -#define Rx7 0x40 /* Rx 7 Bits/Character */ -#define Rx6 0x80 /* Rx 6 Bits/Character */ -#define Rx8 0xc0 /* Rx 8 Bits/Character */ - -/* Write Register 4 */ - -#define PAR_ENA 0x1 /* Parity Enable */ -#define PAR_EVEN 0x2 /* Parity Even/Odd* */ - -#define SYNC_ENAB 0 /* Sync Modes Enable */ -#define SB1 0x4 /* 1 stop bit/char */ -#define SB15 0x8 /* 1.5 stop bits/char */ -#define SB2 0xc /* 2 stop bits/char */ - -#define MONSYNC 0 /* 8 Bit Sync character */ -#define BISYNC 0x10 /* 16 bit sync character */ -#define SDLC 0x20 /* SDLC Mode (01111110 Sync Flag) */ -#define EXTSYNC 0x30 /* External Sync Mode */ - -#define X1CLK 0x0 /* x1 clock mode */ -#define X16CLK 0x40 /* x16 clock mode */ -#define X32CLK 0x80 /* x32 clock mode */ -#define X64CLK 0xC0 /* x64 clock mode */ - -/* Write Register 5 */ - -#define TxCRC_ENAB 0x1 /* Tx CRC Enable */ -#define RTS 0x2 /* RTS */ -#define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENAB 0x8 /* Tx Enable */ -#define SND_BRK 0x10 /* Send Break */ -#define Tx5 0x0 /* Tx 5 bits (or less)/character */ -#define Tx7 0x20 /* Tx 7 bits/character */ -#define Tx6 0x40 /* Tx 6 bits/character */ -#define Tx8 0x60 /* Tx 8 bits/character */ -#define DTR 0x80 /* DTR */ - -/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */ - -/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */ - -/* Write Register 8 (transmit buffer) */ - -/* Write Register 9 (Master interrupt control) */ -#define VIS 1 /* Vector Includes Status */ -#define NV 2 /* No Vector */ -#define DLC 4 /* Disable Lower Chain */ -#define MIE 8 /* Master Interrupt Enable */ -#define STATHI 0x10 /* Status high */ -#define NORESET 0 /* No reset on write to R9 */ -#define CHRB 0x40 /* Reset channel B */ -#define CHRA 0x80 /* Reset channel A */ -#define FHWRES 0xc0 /* Force hardware reset */ - -/* Write Register 10 (misc control bits) */ -#define BIT6 1 /* 6 bit/8bit sync */ -#define LOOPMODE 2 /* SDLC Loop mode */ -#define ABUNDER 4 /* Abort/flag on SDLC xmit underrun */ -#define MARKIDLE 8 /* Mark/flag on idle */ -#define GAOP 0x10 /* Go active on poll */ -#define NRZ 0 /* NRZ mode */ -#define NRZI 0x20 /* NRZI mode */ -#define FM1 0x40 /* FM1 (transition = 1) */ -#define FM0 0x60 /* FM0 (transition = 0) */ -#define CRCPS 0x80 /* CRC Preset I/O */ - -/* Write Register 11 (Clock Mode control) */ -#define TRxCXT 0 /* TRxC = Xtal output */ -#define TRxCTC 1 /* TRxC = Transmit clock */ -#define TRxCBR 2 /* TRxC = BR Generator Output */ -#define TRxCDP 3 /* TRxC = DPLL output */ -#define TRxCOI 4 /* TRxC O/I */ -#define TCRTxCP 0 /* Transmit clock = RTxC pin */ -#define TCTRxCP 8 /* Transmit clock = TRxC pin */ -#define TCBR 0x10 /* Transmit clock = BR Generator output */ -#define TCDPLL 0x18 /* Transmit clock = DPLL output */ -#define RCRTxCP 0 /* Receive clock = RTxC pin */ -#define RCTRxCP 0x20 /* Receive clock = TRxC pin */ -#define RCBR 0x40 /* Receive clock = BR Generator output */ -#define RCDPLL 0x60 /* Receive clock = DPLL output */ -#define RTxCX 0x80 /* RTxC Xtal/No Xtal */ - -/* Write Register 12 (lower byte of baud rate generator time constant) */ - -/* Write Register 13 (upper byte of baud rate generator time constant) */ - -/* Write Register 14 (Misc control bits) */ -#define BRENABL 1 /* Baud rate generator enable */ -#define BRSRC 2 /* Baud rate generator source */ -#define DTRREQ 4 /* DTR/Request function */ -#define AUTOECHO 8 /* Auto Echo */ -#define LOOPBAK 0x10 /* Local loopback */ -#define SEARCH 0x20 /* Enter search mode */ -#define RMC 0x40 /* Reset missing clock */ -#define DISDPLL 0x60 /* Disable DPLL */ -#define SSBR 0x80 /* Set DPLL source = BR generator */ -#define SSRTxC 0xa0 /* Set DPLL source = RTxC */ -#define SFMM 0xc0 /* Set FM mode */ -#define SNRZI 0xe0 /* Set NRZI mode */ - -/* Write Register 15 (external/status interrupt control) */ -#define ZCIE 2 /* Zero count IE */ -#define DCDIE 8 /* DCD IE */ -#define SYNCIE 0x10 /* Sync/hunt IE */ -#define CTSIE 0x20 /* CTS IE */ -#define TxUIE 0x40 /* Tx Underrun/EOM IE */ -#define BRKIE 0x80 /* Break/Abort IE */ - - -/* Read Register 0 */ -#define Rx_CH_AV 0x1 /* Rx Character Available */ -#define ZCOUNT 0x2 /* Zero count */ -#define Tx_BUF_EMP 0x4 /* Tx Buffer empty */ -#define DCD 0x8 /* DCD */ -#define SYNC_HUNT 0x10 /* Sync/hunt */ -#define CTS 0x20 /* CTS */ -#define TxEOM 0x40 /* Tx underrun */ -#define BRK_ABRT 0x80 /* Break/Abort */ - -/* Read Register 1 */ -#define ALL_SNT 0x1 /* All sent */ -/* Residue Data for 8 Rx bits/char programmed */ -#define RES3 0x8 /* 0/3 */ -#define RES4 0x4 /* 0/4 */ -#define RES5 0xc /* 0/5 */ -#define RES6 0x2 /* 0/6 */ -#define RES7 0xa /* 0/7 */ -#define RES8 0x6 /* 0/8 */ -#define RES18 0xe /* 1/8 */ -#define RES28 0x0 /* 2/8 */ -/* Special Rx Condition Interrupts */ -#define PAR_ERR 0x10 /* Parity error */ -#define Rx_OVR 0x20 /* Rx Overrun Error */ -#define CRC_ERR 0x40 /* CRC/Framing Error */ -#define END_FR 0x80 /* End of Frame (SDLC) */ - -/* Read Register 2 (channel b only) - Interrupt vector */ - -/* Read Register 3 (interrupt pending register) ch a only */ -#define CHBEXT 0x1 /* Channel B Ext/Stat IP */ -#define CHBTxIP 0x2 /* Channel B Tx IP */ -#define CHBRxIP 0x4 /* Channel B Rx IP */ -#define CHAEXT 0x8 /* Channel A Ext/Stat IP */ -#define CHATxIP 0x10 /* Channel A Tx IP */ -#define CHARxIP 0x20 /* Channel A Rx IP */ - -/* Read Register 8 (receive data register) */ - -/* Read Register 10 (misc status bits) */ -#define ONLOOP 2 /* On loop */ -#define LOOPSEND 0x10 /* Loop sending */ -#define CLK2MIS 0x40 /* Two clocks missing */ -#define CLK1MIS 0x80 /* One clock missing */ - -/* Read Register 12 (lower byte of baud rate generator constant) */ - -/* Read Register 13 (upper byte of baud rate generator constant) */ - -/* Read Register 15 (value of WR 15) */ - -/* Z85C30/Z85230 Enhanced SCC register definitions */ - -/* Write Register 7' (SDLC/HDLC Programmable Enhancements) */ -#define AUTOTXF 0x01 /* Auto Tx Flag */ -#define AUTOEOM 0x02 /* Auto EOM Latch Reset */ -#define AUTORTS 0x04 /* Auto RTS */ -#define TXDNRZI 0x08 /* TxD Pulled High in SDLC NRZI mode */ -#define RXFIFOH 0x08 /* Z85230: Int on RX FIFO half full */ -#define FASTDTR 0x10 /* Fast DTR/REQ Mode */ -#define CRCCBCR 0x20 /* CRC Check Bytes Completely Received */ -#define TXFIFOE 0x20 /* Z85230: Int on TX FIFO completely empty */ -#define EXTRDEN 0x40 /* Extended Read Enabled */ - -/* Write Register 15 (external/status interrupt control) */ -#define SHDLCE 1 /* SDLC/HDLC Enhancements Enable */ -#define FIFOE 4 /* FIFO Enable */ - -/* Read Register 6 (frame status FIFO) */ -#define BCLSB 0xff /* LSB of 14 bits count */ - -/* Read Register 7 (frame status FIFO) */ -#define BCMSB 0x3f /* MSB of 14 bits count */ -#define FDA 0x40 /* FIFO Data Available Status */ -#define FOS 0x80 /* FIFO Overflow Status */ diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 6147ee8b1d78..f904f4d16b45 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -26,6 +26,8 @@ #include <uapi/linux/if_macsec.h> +static struct workqueue_struct *macsec_wq; + /* SecTAG length = macsec_eth_header without the optional SCI */ #define MACSEC_TAG_LEN 6 @@ -174,9 +176,10 @@ static void macsec_rxsc_put(struct macsec_rx_sc *sc) call_rcu(&sc->rcu_head, free_rx_sc_rcu); } -static void free_rxsa(struct rcu_head *head) +static void free_rxsa_work(struct work_struct *work) { - struct macsec_rx_sa *sa = container_of(head, struct macsec_rx_sa, rcu); + struct macsec_rx_sa *sa = + container_of(to_rcu_work(work), struct macsec_rx_sa, destroy_work); crypto_free_aead(sa->key.tfm); free_percpu(sa->stats); @@ -186,7 +189,7 @@ static void free_rxsa(struct rcu_head *head) static void macsec_rxsa_put(struct macsec_rx_sa *sa) { if (refcount_dec_and_test(&sa->refcnt)) - call_rcu(&sa->rcu, free_rxsa); + queue_rcu_work(macsec_wq, &sa->destroy_work); } static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr) @@ -202,9 +205,10 @@ static struct macsec_tx_sa *macsec_txsa_get(struct macsec_tx_sa __rcu *ptr) return sa; } -static void free_txsa(struct rcu_head *head) +static void free_txsa_work(struct work_struct *work) { - struct macsec_tx_sa *sa = container_of(head, struct macsec_tx_sa, rcu); + struct macsec_tx_sa *sa = + container_of(to_rcu_work(work), struct macsec_tx_sa, destroy_work); crypto_free_aead(sa->key.tfm); free_percpu(sa->stats); @@ -214,7 +218,7 @@ static void free_txsa(struct rcu_head *head) static void macsec_txsa_put(struct macsec_tx_sa *sa) { if (refcount_dec_and_test(&sa->refcnt)) - call_rcu(&sa->rcu, free_txsa); + queue_rcu_work(macsec_wq, &sa->destroy_work); } static struct macsec_cb *macsec_skb_cb(struct sk_buff *skb) @@ -1407,6 +1411,7 @@ static int init_rx_sa(struct macsec_rx_sa *rx_sa, char *sak, int key_len, rx_sa->next_pn = 1; refcount_set(&rx_sa->refcnt, 1); spin_lock_init(&rx_sa->lock); + INIT_RCU_WORK(&rx_sa->destroy_work, free_rxsa_work); return 0; } @@ -1506,6 +1511,7 @@ static int init_tx_sa(struct macsec_tx_sa *tx_sa, char *sak, int key_len, tx_sa->active = false; refcount_set(&tx_sa->refcnt, 1); spin_lock_init(&tx_sa->lock); + INIT_RCU_WORK(&tx_sa->destroy_work, free_txsa_work); return 0; } @@ -4505,25 +4511,35 @@ static int __init macsec_init(void) { int err; + macsec_wq = alloc_workqueue("macsec", WQ_UNBOUND, 0); + if (!macsec_wq) + return -ENOMEM; + pr_info("MACsec IEEE 802.1AE\n"); err = register_netdevice_notifier(&macsec_notifier); if (err) - return err; + goto err_destroy_wq; err = rtnl_link_register(&macsec_link_ops); if (err) - goto notifier; + goto err_notifier; err = genl_register_family(&macsec_fam); if (err) - goto rtnl; + goto err_rtnl; return 0; -rtnl: +err_rtnl: rtnl_link_unregister(&macsec_link_ops); -notifier: +err_notifier: unregister_netdevice_notifier(&macsec_notifier); +err_destroy_wq: + /* Precautionary, mirrors macsec_exit() to stay safe if work + * ever becomes queueable before this point in the future. + */ + rcu_barrier(); + destroy_workqueue(macsec_wq); return err; } @@ -4533,6 +4549,7 @@ static void __exit macsec_exit(void) rtnl_link_unregister(&macsec_link_ops); unregister_netdevice_notifier(&macsec_notifier); rcu_barrier(); + destroy_workqueue(macsec_wq); } module_init(macsec_init); diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 9f90c598649d..c40fa331836b 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1689,6 +1689,7 @@ static size_t macvlan_get_size(const struct net_device *dev) + macvlan_get_size_mac(vlan) /* IFLA_MACVLAN_MACADDR */ + nla_total_size(4) /* IFLA_MACVLAN_BC_QUEUE_LEN */ + nla_total_size(4) /* IFLA_MACVLAN_BC_QUEUE_LEN_USED */ + + nla_total_size(4) /* IFLA_MACVLAN_BC_CUTOFF */ ); } diff --git a/drivers/net/mctp/mctp-i2c.c b/drivers/net/mctp/mctp-i2c.c index 15fe4d1163c1..ee2913758e54 100644 --- a/drivers/net/mctp/mctp-i2c.c +++ b/drivers/net/mctp/mctp-i2c.c @@ -496,8 +496,6 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb) u8 *pecp; int rc; - fs = mctp_i2c_get_tx_flow_state(midev, skb); - hdr = (void *)skb_mac_header(skb); /* Sanity check that packet contents matches skb length, * and can't exceed MCTP_I2C_BUFSZ @@ -509,6 +507,8 @@ static void mctp_i2c_xmit(struct mctp_i2c_dev *midev, struct sk_buff *skb) return; } + fs = mctp_i2c_get_tx_flow_state(midev, skb); + if (skb_tailroom(skb) >= 1) { /* Linear case with space, we can just append the PEC */ skb_put(skb, 1); diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig index 516b0d05e16e..c71132f33f84 100644 --- a/drivers/net/mdio/Kconfig +++ b/drivers/net/mdio/Kconfig @@ -147,6 +147,7 @@ config MDIO_OCTEON config MDIO_PIC64HPSC tristate "PIC64-HPSC/HX MDIO interface support" + depends on ARCH_MICROCHIP || COMPILE_TEST depends on HAS_IOMEM && OF_MDIO help This driver supports the MDIO interface found on the PIC64-HPSC/HX diff --git a/drivers/net/net_failover.c b/drivers/net/net_failover.c index d0361aaf25ef..3f7d31033bae 100644 --- a/drivers/net/net_failover.c +++ b/drivers/net/net_failover.c @@ -502,7 +502,7 @@ static int net_failover_slave_register(struct net_device *slave_dev, /* Align MTU of slave with failover dev */ orig_mtu = slave_dev->mtu; - err = dev_set_mtu(slave_dev, failover_dev->mtu); + err = netif_set_mtu(slave_dev, failover_dev->mtu); if (err) { netdev_err(failover_dev, "unable to change mtu of %s to %u register failed\n", slave_dev->name, failover_dev->mtu); @@ -512,11 +512,11 @@ static int net_failover_slave_register(struct net_device *slave_dev, dev_hold(slave_dev); if (netif_running(failover_dev)) { - err = dev_open(slave_dev, NULL); + err = netif_open(slave_dev, NULL); if (err && (err != -EBUSY)) { netdev_err(failover_dev, "Opening slave %s failed err:%d\n", slave_dev->name, err); - goto err_dev_open; + goto err_netif_open; } } @@ -562,10 +562,10 @@ static int net_failover_slave_register(struct net_device *slave_dev, err_vlan_add: dev_uc_unsync(slave_dev, failover_dev); dev_mc_unsync(slave_dev, failover_dev); - dev_close(slave_dev); -err_dev_open: + netif_close(slave_dev); +err_netif_open: dev_put(slave_dev); - dev_set_mtu(slave_dev, orig_mtu); + netif_set_mtu(slave_dev, orig_mtu); done: return err; } diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 3c9acd6e49e8..57dd6821a8aa 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -497,6 +497,8 @@ static void trim_newline(char *s, size_t maxlen) size_t len; len = strnlen(s, maxlen); + if (!len) + return; if (s[len - 1] == '\n') s[len - 1] = '\0'; } @@ -750,7 +752,7 @@ static ssize_t enabled_store(struct config_item *item, unregister_netcons_consoles(); } - ret = strnlen(buf, count); + ret = count; /* Deferred cleanup */ netconsole_process_cleanups(); out_unlock: @@ -779,7 +781,7 @@ static ssize_t release_store(struct config_item *item, const char *buf, nt->release = release; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -805,7 +807,7 @@ static ssize_t extended_store(struct config_item *item, const char *buf, goto out_unlock; nt->extended = extended; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -815,6 +817,13 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf, size_t count) { struct netconsole_target *nt = to_target(item); + size_t len = count; + + /* Account for a trailing newline appended by tools like echo */ + if (len && buf[len - 1] == '\n') + len--; + if (len >= IFNAMSIZ) + return -ENAMETOOLONG; dynamic_netconsole_mutex_lock(); if (nt->state == STATE_ENABLED) { @@ -828,7 +837,7 @@ static ssize_t dev_name_store(struct config_item *item, const char *buf, trim_newline(nt->np.dev_name, IFNAMSIZ); dynamic_netconsole_mutex_unlock(); - return strnlen(buf, count); + return count; } static ssize_t local_port_store(struct config_item *item, const char *buf, @@ -847,7 +856,7 @@ static ssize_t local_port_store(struct config_item *item, const char *buf, ret = kstrtou16(buf, 10, &nt->np.local_port); if (ret < 0) goto out_unlock; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -869,7 +878,7 @@ static ssize_t remote_port_store(struct config_item *item, ret = kstrtou16(buf, 10, &nt->np.remote_port); if (ret < 0) goto out_unlock; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -894,7 +903,7 @@ static ssize_t local_ip_store(struct config_item *item, const char *buf, goto out_unlock; nt->np.ipv6 = !!ipv6; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -919,7 +928,7 @@ static ssize_t remote_ip_store(struct config_item *item, const char *buf, goto out_unlock; nt->np.ipv6 = !!ipv6; - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -955,7 +964,7 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf, goto out_unlock; memcpy(nt->np.remote_mac, remote_mac, ETH_ALEN); - ret = strnlen(buf, count); + ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); return ret; @@ -1070,26 +1079,30 @@ static ssize_t userdatum_value_store(struct config_item *item, const char *buf, size_t count) { struct userdatum *udm = to_userdatum(item); + char old_value[MAX_EXTRADATA_VALUE_LEN]; struct netconsole_target *nt; struct userdata *ud; ssize_t ret; - if (count > MAX_EXTRADATA_VALUE_LEN) + if (count >= MAX_EXTRADATA_VALUE_LEN) return -EMSGSIZE; mutex_lock(&netconsole_subsys.su_mutex); dynamic_netconsole_mutex_lock(); - - ret = strscpy(udm->value, buf, sizeof(udm->value)); - if (ret < 0) - goto out_unlock; + /* Snapshot for rollback if update_userdata() fails below */ + strscpy(old_value, udm->value, sizeof(old_value)); + /* count is bounded above, so strscpy() cannot truncate here */ + strscpy(udm->value, buf, sizeof(udm->value)); trim_newline(udm->value, sizeof(udm->value)); ud = to_userdata(item->ci_parent); nt = userdata_to_target(ud); ret = update_userdata(nt); - if (ret < 0) + if (ret < 0) { + /* Restore the previous value so it matches the live payload */ + strscpy(udm->value, old_value, sizeof(udm->value)); goto out_unlock; + } ret = count; out_unlock: dynamic_netconsole_mutex_unlock(); @@ -1131,7 +1144,7 @@ static ssize_t sysdata_msgid_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_MSGID); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; @@ -1160,7 +1173,7 @@ static ssize_t sysdata_release_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_RELEASE); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; @@ -1189,7 +1202,7 @@ static ssize_t sysdata_taskname_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_TASKNAME); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; @@ -1223,7 +1236,7 @@ static ssize_t sysdata_cpu_nr_enabled_store(struct config_item *item, disable_sysdata_feature(nt, SYSDATA_CPU_NR); unlock_ok: - ret = strnlen(buf, count); + ret = count; dynamic_netconsole_mutex_unlock(); mutex_unlock(&netconsole_subsys.su_mutex); return ret; diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 1e06e781c835..f00fc2f9ebde 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -829,7 +829,7 @@ static struct sk_buff *nsim_dev_trap_skb_build(void) skb->protocol = htons(ETH_P_IP); skb_set_network_header(skb, skb->len); - iph = skb_put(skb, sizeof(struct iphdr)); + iph = skb_put_zero(skb, sizeof(struct iphdr)); iph->protocol = IPPROTO_UDP; iph->saddr = in_aton("192.0.2.1"); iph->daddr = in_aton("198.51.100.1"); diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index e1541ca76715..a750768912b5 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -185,7 +185,9 @@ out_drop_cnt: return NETDEV_TX_OK; } -static void nsim_set_rx_mode(struct net_device *dev) +static void nsim_set_rx_mode(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { } @@ -623,7 +625,7 @@ static const struct net_shaper_ops nsim_shaper_ops = { static const struct net_device_ops nsim_netdev_ops = { .ndo_start_xmit = nsim_start_xmit, - .ndo_set_rx_mode = nsim_set_rx_mode, + .ndo_set_rx_mode_async = nsim_set_rx_mode, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, @@ -648,7 +650,7 @@ static const struct net_device_ops nsim_netdev_ops = { static const struct net_device_ops nsim_vf_netdev_ops = { .ndo_start_xmit = nsim_start_xmit, - .ndo_set_rx_mode = nsim_set_rx_mode, + .ndo_set_rx_mode_async = nsim_set_rx_mode, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = nsim_change_mtu, @@ -1180,7 +1182,8 @@ void nsim_destroy(struct netdevsim *ns) unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb, &ns->nn); - nsim_psp_uninit(ns); + if (nsim_dev_port_is_pf(ns->nsim_dev_port)) + nsim_psp_uninit(ns); rtnl_lock(); peer = rtnl_dereference(ns->peer); diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 7e129dddbbe7..d909c4160ea1 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -120,7 +120,9 @@ struct netdevsim { u64_stats_t tx_packets; u64_stats_t tx_bytes; struct u64_stats_sync syncp; - struct psp_dev *dev; + struct psp_dev __rcu *dev; + struct dentry *rereg; + struct mutex rereg_lock; u32 spi; u32 assoc_cnt; } psp; diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c index 0b4d717253b0..6936ecb8173e 100644 --- a/drivers/net/netdevsim/psp.c +++ b/drivers/net/netdevsim/psp.c @@ -19,6 +19,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, struct netdevsim *peer_ns, struct skb_ext **psp_ext) { enum skb_drop_reason rc = 0; + struct psp_dev *peer_psd; struct psp_assoc *pas; struct net *net; void **ptr; @@ -48,7 +49,8 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, } /* Now pretend we just received this frame */ - if (peer_ns->psp.dev->config.versions & (1 << pas->version)) { + peer_psd = rcu_dereference(peer_ns->psp.dev); + if (peer_psd && peer_psd->config.versions & (1 << pas->version)) { bool strip_icv = false; u8 generation; @@ -61,8 +63,7 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns, skb_ext_reset(skb); skb->mac_len = ETH_HLEN; - if (psp_dev_rcv(skb, peer_ns->psp.dev->id, generation, - strip_icv)) { + if (psp_dev_rcv(skb, peer_psd->id, generation, strip_icv)) { rc = SKB_DROP_REASON_PSP_OUTPUT; goto out_unlock; } @@ -209,26 +210,50 @@ static struct psp_dev_caps nsim_psp_caps = { .assoc_drv_spc = sizeof(void *), }; -void nsim_psp_uninit(struct netdevsim *ns) +static void __nsim_psp_uninit(struct netdevsim *ns, bool teardown) { - if (!IS_ERR(ns->psp.dev)) - psp_dev_unregister(ns->psp.dev); + struct psp_dev *psd; + + psd = rcu_dereference_protected(ns->psp.dev, + teardown || + lockdep_is_held(&ns->psp.rereg_lock)); + if (psd) { + rcu_assign_pointer(ns->psp.dev, NULL); + synchronize_rcu(); + psp_dev_unregister(psd); + } WARN_ON(ns->psp.assoc_cnt); } +void nsim_psp_uninit(struct netdevsim *ns) +{ + debugfs_remove(ns->psp.rereg); + mutex_destroy(&ns->psp.rereg_lock); + __nsim_psp_uninit(ns, true); +} + static ssize_t nsim_psp_rereg_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { struct netdevsim *ns = file->private_data; - int err; + struct psp_dev *psd; + ssize_t ret; + + mutex_lock(&ns->psp.rereg_lock); + __nsim_psp_uninit(ns, false); - nsim_psp_uninit(ns); + psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns); + if (IS_ERR(psd)) { + ret = PTR_ERR(psd); + goto out; + } - ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops, - &nsim_psp_caps, ns); - err = PTR_ERR_OR_ZERO(ns->psp.dev); - return err ?: count; + rcu_assign_pointer(ns->psp.dev, psd); + ret = count; +out: + mutex_unlock(&ns->psp.rereg_lock); + return ret; } static const struct file_operations nsim_psp_rereg_fops = { @@ -241,14 +266,16 @@ static const struct file_operations nsim_psp_rereg_fops = { int nsim_psp_init(struct netdevsim *ns) { struct dentry *ddir = ns->nsim_dev_port->ddir; - int err; + struct psp_dev *psd; + + psd = psp_dev_create(ns->netdev, &nsim_psp_ops, &nsim_psp_caps, ns); + if (IS_ERR(psd)) + return PTR_ERR(psd); - ns->psp.dev = psp_dev_create(ns->netdev, &nsim_psp_ops, - &nsim_psp_caps, ns); - err = PTR_ERR_OR_ZERO(ns->psp.dev); - if (err) - return err; + rcu_assign_pointer(ns->psp.dev, psd); - debugfs_create_file("psp_rereg", 0200, ddir, ns, &nsim_psp_rereg_fops); + mutex_init(&ns->psp.rereg_lock); + ns->psp.rereg = debugfs_create_file("psp_rereg", 0200, ddir, ns, + &nsim_psp_rereg_fops); return 0; } diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c index 7b56a7ad7a49..5e2eecc3165d 100644 --- a/drivers/net/netkit.c +++ b/drivers/net/netkit.c @@ -186,7 +186,9 @@ static int netkit_get_iflink(const struct net_device *dev) return iflink; } -static void netkit_set_multicast(struct net_device *dev) +static void netkit_set_multicast(struct net_device *dev, + struct netdev_hw_addr_list *uc, + struct netdev_hw_addr_list *mc) { /* Nothing to do, we receive whatever gets pushed to us! */ } @@ -330,7 +332,7 @@ static const struct net_device_ops netkit_netdev_ops = { .ndo_open = netkit_open, .ndo_stop = netkit_close, .ndo_start_xmit = netkit_xmit, - .ndo_set_rx_mode = netkit_set_multicast, + .ndo_set_rx_mode_async = netkit_set_multicast, .ndo_set_rx_headroom = netkit_set_headroom, .ndo_set_mac_address = netkit_set_macaddr, .ndo_get_iflink = netkit_get_iflink, diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index db43a1f8a07a..22c555dd962e 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -85,17 +85,24 @@ static void ovpn_netdev_write(struct ovpn_peer *peer, struct sk_buff *skb) skb_scrub_packet(skb, true); /* network header reset in ovpn_decrypt_post() */ + skb_reset_mac_header(skb); skb_reset_transport_header(skb); skb_reset_inner_headers(skb); /* cause packet to be "received" by the interface */ pkt_len = skb->len; + /* we may get here in process context in case of TCP connections, + * therefore we have to disable BHs to ensure gro_cells_receive() + * and dev_dstats_rx_add() do not get corrupted or enter deadlock + */ + local_bh_disable(); ret = gro_cells_receive(&peer->ovpn->gro_cells, skb); if (likely(ret == NET_RX_SUCCESS)) { /* update RX stats with the size of decrypted packet */ ovpn_peer_stats_increment_rx(&peer->vpn_stats, pkt_len); dev_dstats_rx_add(peer->ovpn->dev, pkt_len); } + local_bh_enable(); } void ovpn_decrypt_post(void *data, int ret) diff --git a/drivers/net/phy/bcm-phy-lib.c b/drivers/net/phy/bcm-phy-lib.c index 5198d66dbbc0..b64beade8dd9 100644 --- a/drivers/net/phy/bcm-phy-lib.c +++ b/drivers/net/phy/bcm-phy-lib.c @@ -563,6 +563,15 @@ void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, } EXPORT_SYMBOL_GPL(bcm_phy_get_stats); +void bcm_phy_update_stats_shadow(struct phy_device *phydev, u64 *shadow) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++) + bcm_phy_get_stat(phydev, shadow, i); +} +EXPORT_SYMBOL_GPL(bcm_phy_update_stats_shadow); + void bcm_phy_r_rc_cal_reset(struct phy_device *phydev) { /* Reset R_CAL/RC_CAL Engine */ diff --git a/drivers/net/phy/bcm-phy-lib.h b/drivers/net/phy/bcm-phy-lib.h index bceddbc860eb..bba94ce96195 100644 --- a/drivers/net/phy/bcm-phy-lib.h +++ b/drivers/net/phy/bcm-phy-lib.h @@ -85,6 +85,7 @@ int bcm_phy_get_sset_count(struct phy_device *phydev); void bcm_phy_get_strings(struct phy_device *phydev, u8 *data); void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow, struct ethtool_stats *stats, u64 *data); +void bcm_phy_update_stats_shadow(struct phy_device *phydev, u64 *shadow); void bcm_phy_r_rc_cal_reset(struct phy_device *phydev); int bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev); int bcm_phy_enable_jumbo(struct phy_device *phydev); diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index 00e8fa14aa77..71a163f62c0e 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -807,6 +807,17 @@ static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev, bcm_phy_get_stats(phydev, priv->stats, stats, data); } +static int bcm7xxx_28nm_suspend(struct phy_device *phydev) +{ + struct bcm7xxx_phy_priv *priv = phydev->priv; + + mutex_lock(&phydev->lock); + bcm_phy_update_stats_shadow(phydev, priv->stats); + mutex_unlock(&phydev->lock); + + return genphy_suspend(phydev); +} + static int bcm7xxx_28nm_probe(struct phy_device *phydev) { struct bcm7xxx_phy_priv *priv; @@ -849,6 +860,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .flags = PHY_IS_INTERNAL, \ .config_init = bcm7xxx_28nm_config_init, \ .resume = bcm7xxx_28nm_resume, \ + .suspend = bcm7xxx_28nm_suspend, \ .get_tunable = bcm7xxx_28nm_get_tunable, \ .set_tunable = bcm7xxx_28nm_set_tunable, \ .get_sset_count = bcm_phy_get_sset_count, \ @@ -866,6 +878,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .flags = PHY_IS_INTERNAL, \ .config_init = bcm7xxx_28nm_ephy_config_init, \ .resume = bcm7xxx_28nm_ephy_resume, \ + .suspend = bcm7xxx_28nm_suspend, \ .get_sset_count = bcm_phy_get_sset_count, \ .get_strings = bcm_phy_get_strings, \ .get_stats = bcm7xxx_28nm_get_phy_stats, \ @@ -902,6 +915,7 @@ static int bcm7xxx_28nm_probe(struct phy_device *phydev) .config_aneg = genphy_config_aneg, \ .read_status = genphy_read_status, \ .resume = bcm7xxx_16nm_ephy_resume, \ + .suspend = bcm7xxx_28nm_suspend, \ } static struct phy_driver bcm7xxx_driver[] = { diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index bf0c6a04481e..d1a4edb34ad2 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -592,8 +592,13 @@ static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state) static int bcm54xx_suspend(struct phy_device *phydev) { + struct bcm54xx_phy_priv *priv = phydev->priv; int ret = 0; + mutex_lock(&phydev->lock); + bcm_phy_update_stats_shadow(phydev, priv->stats); + mutex_unlock(&phydev->lock); + bcm54xx_ptp_stop(phydev); /* Acknowledge any Wake-on-LAN interrupt prior to suspend */ diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c index 1f381d7b13ff..96a7d255f50f 100644 --- a/drivers/net/phy/dp83869.c +++ b/drivers/net/phy/dp83869.c @@ -31,6 +31,7 @@ #define DP83869_RGMIICTL 0x0032 #define DP83869_STRAP_STS1 0x006e #define DP83869_RGMIIDCTL 0x0086 +#define DP83869_ANA_PLL_PROG_PI 0x00c6 #define DP83869_RXFCFG 0x0134 #define DP83869_RXFPMD1 0x0136 #define DP83869_RXFPMD2 0x0137 @@ -826,12 +827,22 @@ static int dp83869_config_init(struct phy_device *phydev) dp83869_config_port_mirroring(phydev); /* Clock output selection if muxing property is set */ - if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) + if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK) { + /* + * Table 7-121 in datasheet says we have to set register 0xc6 + * to value 0x10 before CLK_O_SEL can be modified. + */ + ret = phy_write_mmd(phydev, DP83869_DEVADDR, + DP83869_ANA_PLL_PROG_PI, 0x10); + if (ret) + return ret; + ret = phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_IO_MUX_CFG, DP83869_IO_MUX_CFG_CLK_O_SEL_MASK, dp83869->clk_output_sel << DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT); + } if (phy_interface_is_rgmii(phydev)) { ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL, diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c index e480c2a07450..252fb12b3e68 100644 --- a/drivers/net/phy/dp83tc811.c +++ b/drivers/net/phy/dp83tc811.c @@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = { .config_init = dp83811_config_init, .config_aneg = dp83811_config_aneg, .soft_reset = dp83811_phy_reset, + .get_features = genphy_c45_pma_read_ext_abilities, .get_wol = dp83811_get_wol, .set_wol = dp83811_set_wol, .config_intr = dp83811_config_intr, diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 2aa1dedd21b8..e211a523c258 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -4548,6 +4548,13 @@ static int lan8814_config_init(struct phy_device *phydev) struct kszphy_priv *lan8814 = phydev->priv; int ret; + if (phy_package_init_once(phydev)) + /* Reset the PHY */ + lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, + LAN8814_QSGMII_SOFT_RESET, + LAN8814_QSGMII_SOFT_RESET_BIT, + LAN8814_QSGMII_SOFT_RESET_BIT); + /* Based on the interface type select how the advertise ability is * encoded, to set as SGMII or as USGMII. */ @@ -4655,13 +4662,7 @@ static int lan8814_probe(struct phy_device *phydev) priv->is_ptp_available = err == LAN8814_REV_LAN8814 || err == LAN8814_REV_LAN8818; - if (phy_package_init_once(phydev)) { - /* Reset the PHY */ - lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, - LAN8814_QSGMII_SOFT_RESET, - LAN8814_QSGMII_SOFT_RESET_BIT, - LAN8814_QSGMII_SOFT_RESET_BIT); - + if (phy_package_probe_once(phydev)) { err = lan8814_release_coma_mode(phydev); if (err) return err; diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index b0d3bc49c685..57c68efa5ff8 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -2245,7 +2245,7 @@ ppp_do_recv(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) */ static void __ppp_decompress_proto(struct sk_buff *skb) { - if (skb->data[0] & 0x01) + if (ppp_skb_is_compressed_proto(skb)) *(u8 *)skb_push(skb, 1) = 0x00; } diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c index d546a7af0d54..bdd61c504a1c 100644 --- a/drivers/net/ppp/pppoe.c +++ b/drivers/net/ppp/pppoe.c @@ -393,7 +393,7 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, if (skb_mac_header_len(skb) < ETH_HLEN) goto drop; - if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr))) + if (!pskb_may_pull(skb, PPPOE_SES_HLEN)) goto drop; ph = pppoe_hdr(skb); @@ -403,6 +403,12 @@ static int pppoe_rcv(struct sk_buff *skb, struct net_device *dev, if (skb->len < len) goto drop; + /* skb->data points to the PPP protocol header after skb_pull_rcsum. + * Drop PFC frames. + */ + if (ppp_skb_is_compressed_proto(skb)) + goto drop; + if (pskb_trim_rcsum(skb, len)) goto drop; diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index f6b94ac7a68a..87aa4f4e9724 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -1170,6 +1170,7 @@ struct pse_irq { struct pse_controller_dev *pcdev; struct pse_irq_desc desc; unsigned long *notifs; + unsigned long *notifs_mask; }; /** @@ -1247,7 +1248,6 @@ static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id, static irqreturn_t pse_isr(int irq, void *data) { struct pse_controller_dev *pcdev; - unsigned long notifs_mask = 0; struct pse_irq_desc *desc; struct pse_irq *h = data; int ret, i; @@ -1257,14 +1257,15 @@ static irqreturn_t pse_isr(int irq, void *data) /* Clear notifs mask */ memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); + bitmap_zero(h->notifs_mask, pcdev->nr_lines); mutex_lock(&pcdev->lock); - ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask); - if (ret || !notifs_mask) { + ret = desc->map_event(irq, pcdev, h->notifs, h->notifs_mask); + if (ret || bitmap_empty(h->notifs_mask, pcdev->nr_lines)) { mutex_unlock(&pcdev->lock); return IRQ_NONE; } - for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) { + for_each_set_bit(i, h->notifs_mask, pcdev->nr_lines) { unsigned long notifs, rnotifs; struct pse_ntf ntf = {}; @@ -1340,6 +1341,10 @@ int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq, if (!h->notifs) return -ENOMEM; + h->notifs_mask = devm_bitmap_zalloc(dev, pcdev->nr_lines, GFP_KERNEL); + if (!h->notifs_mask) + return -ENOMEM; + ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr, IRQF_ONESHOT | irq_flags, irq_name, h); diff --git a/drivers/net/slip/slhc.c b/drivers/net/slip/slhc.c index e3c785da3eef..1a9b27d5e256 100644 --- a/drivers/net/slip/slhc.c +++ b/drivers/net/slip/slhc.c @@ -80,9 +80,9 @@ #include <linux/unaligned.h> static unsigned char *encode(unsigned char *cp, unsigned short n); -static long decode(unsigned char **cpp); +static long decode(unsigned char **cpp, const unsigned char *end); static unsigned char * put16(unsigned char *cp, unsigned short x); -static unsigned short pull16(unsigned char **cpp); +static long pull16(unsigned char **cpp, const unsigned char *end); /* Allocate compression data structure * slots must be in range 0 to 255 (zero meaning no compression) @@ -190,30 +190,34 @@ encode(unsigned char *cp, unsigned short n) return cp; } -/* Pull a 16-bit integer in host order from buffer in network byte order */ -static unsigned short -pull16(unsigned char **cpp) +/* Pull a 16-bit integer in host order from buffer in network byte order. + * Returns -1 if the buffer is exhausted, otherwise the 16-bit value. + */ +static long +pull16(unsigned char **cpp, const unsigned char *end) { - short rval; + long rval; + if (*cpp + 2 > end) + return -1; rval = *(*cpp)++; rval <<= 8; rval |= *(*cpp)++; return rval; } -/* Decode a number */ +/* Decode a number. Returns -1 if the buffer is exhausted. */ static long -decode(unsigned char **cpp) +decode(unsigned char **cpp, const unsigned char *end) { int x; + if (*cpp >= end) + return -1; x = *(*cpp)++; - if(x == 0){ - return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ - } else { - return x & 0xff; /* -1 if PULLCHAR returned error */ - } + if (x == 0) + return pull16(cpp, end); + return x & 0xff; } /* @@ -499,6 +503,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) struct cstate *cs; int len, hdrlen; unsigned char *cp = icp; + const unsigned char *end = icp + isize; /* We've got a compressed packet; read the change byte */ comp->sls_i_compressed++; @@ -506,6 +511,8 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) comp->sls_i_error++; return 0; } + if (!comp->rstate) + goto bad; changes = *cp++; if(changes & NEW_C){ /* Make sure the state index is in range, then grab the state. @@ -534,6 +541,8 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) thp = &cs->cs_tcp; ip = &cs->cs_ip; + if (cp + 2 > end) + goto bad; thp->check = *(__sum16 *)cp; cp += 2; @@ -564,26 +573,26 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) default: if(changes & NEW_U){ thp->urg = 1; - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->urg_ptr = htons(x); } else thp->urg = 0; if(changes & NEW_W){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->window = htons( ntohs(thp->window) + x); } if(changes & NEW_A){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->ack_seq = htonl( ntohl(thp->ack_seq) + x); } if(changes & NEW_S){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } thp->seq = htonl( ntohl(thp->seq) + x); @@ -591,7 +600,7 @@ slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) break; } if(changes & NEW_I){ - if((x = decode(&cp)) == -1) { + if((x = decode(&cp, end)) == -1) { goto bad; } ip->id = htons (ntohs (ip->id) + x); @@ -649,6 +658,10 @@ slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) struct cstate *cs; unsigned int ihl; + if (!comp->rstate) { + comp->sls_i_error++; + return slhc_toss(comp); + } /* The packet is shorter than a legal IP header. * Also make sure isize is positive. */ diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index df0bcfedddbc..293ef80c4e30 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -756,6 +756,7 @@ static void ax88772_mac_link_down(struct phylink_config *config, struct usbnet *dev = netdev_priv(to_net_dev(config->dev)); asix_write_medium_mode(dev, 0, 0); + usbnet_link_change(dev, false, false); } static void ax88772_mac_link_up(struct phylink_config *config, @@ -786,6 +787,7 @@ static void ax88772_mac_link_up(struct phylink_config *config, m |= AX_MEDIUM_RFC; asix_write_medium_mode(dev, m, 0); + usbnet_link_change(dev, true, false); } static const struct phylink_mac_ops ax88772_phylink_mac_ops = { diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index bb9929727eb9..0223a172851e 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -2012,6 +2012,14 @@ static const struct usb_device_id cdc_devs[] = { .driver_info = (unsigned long)&apple_private_interface_info, }, + /* Mac */ + { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1905, 0), + .driver_info = (unsigned long)&apple_private_interface_info, + }, + { USB_DEVICE_INTERFACE_NUMBER(0x05ac, 0x1905, 2), + .driver_info = (unsigned long)&apple_private_interface_info, + }, + /* Ericsson MBM devices like F5521gw */ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_VENDOR, diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 7337bf1b7d6a..1ace1d2398c9 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -10138,6 +10138,7 @@ static const struct usb_device_id rtl8152_table[] = { { USB_DEVICE(VENDOR_ID_DELL, 0xb097) }, { USB_DEVICE(VENDOR_ID_ASUS, 0x1976) }, { USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02b) }, + { USB_DEVICE(VENDOR_ID_TRENDNET, 0xe02c) }, {} }; diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c index 4cda0643afb6..c880c95c41a5 100644 --- a/drivers/net/usb/rtl8150.c +++ b/drivers/net/usb/rtl8150.c @@ -683,6 +683,7 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev) { rtl8150_t *dev = netdev_priv(netdev); + unsigned int skb_len; int count, res; /* pad the frame and ensure terminating USB packet, datasheet 9.2.3 */ @@ -694,6 +695,8 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } + skb_len = skb->len; + netif_stop_queue(netdev); dev->tx_skb = skb; usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2), @@ -707,9 +710,16 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb, netdev->stats.tx_errors++; netif_start_queue(netdev); } + /* + * The URB was not submitted, so write_bulk_callback() will + * never run to free dev->tx_skb. Drop the skb here and + * clear tx_skb to avoid leaving a stale pointer. + */ + dev->tx_skb = NULL; + dev_kfree_skb_any(skb); } else { netdev->stats.tx_packets++; - netdev->stats.tx_bytes += skb->len; + netdev->stats.tx_bytes += skb_len; netif_trans_update(netdev); } diff --git a/drivers/net/veth.c b/drivers/net/veth.c index e35df717e65e..0cfb19b760dd 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -972,7 +972,8 @@ static int veth_poll(struct napi_struct *napi, int budget) /* NAPI functions as RCU section */ peer_dev = rcu_dereference_check(priv->peer, rcu_read_lock_bh_held()); - peer_txq = peer_dev ? netdev_get_tx_queue(peer_dev, queue_idx) : NULL; + peer_txq = (peer_dev && queue_idx < peer_dev->real_num_tx_queues) ? + netdev_get_tx_queue(peer_dev, queue_idx) : NULL; xdp_set_return_frame_no_direct(); done = veth_xdp_rcv(rq, budget, &bq, &stats); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index bfb566fecb92..f4adcfee7a80 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -3759,6 +3759,12 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) queue_pairs); return -EINVAL; } + + /* Keep max_tx_vq in sync so that a later RSS command does not + * revert queue_pairs to a stale value. + */ + if (vi->has_rss) + vi->rss_trailer.max_tx_vq = cpu_to_le16(queue_pairs); succ: vi->curr_queue_pairs = queue_pairs; if (dev->flags & IFF_UP) { diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 2cf2dbd1c12f..46209917ae4d 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -1034,6 +1034,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev, err: port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; + synchronize_net(); return ret; } @@ -1053,10 +1054,16 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev, } /* inverse of do_vrf_add_slave */ -static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) +static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev, + bool needs_sync) { netdev_upper_dev_unlink(port_dev, dev); port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE; + /* Make sure that concurrent RCU readers that identified the device + * as a VRF port see a VRF master or no master at all. + */ + if (needs_sync) + synchronize_net(); cycle_netdev(port_dev, NULL); @@ -1065,7 +1072,7 @@ static int do_vrf_del_slave(struct net_device *dev, struct net_device *port_dev) static int vrf_del_slave(struct net_device *dev, struct net_device *port_dev) { - return do_vrf_del_slave(dev, port_dev); + return do_vrf_del_slave(dev, port_dev, true); } static void vrf_dev_uninit(struct net_device *dev) @@ -1619,7 +1626,7 @@ static void vrf_dellink(struct net_device *dev, struct list_head *head) struct list_head *iter; netdev_for_each_lower_dev(dev, port_dev, iter) - vrf_del_slave(dev, port_dev); + do_vrf_del_slave(dev, port_dev, false); vrf_map_unregister_dev(dev); @@ -1751,7 +1758,7 @@ static int vrf_device_event(struct notifier_block *unused, goto out; vrf_dev = netdev_master_upper_dev_get(dev); - vrf_del_slave(vrf_dev, dev); + do_vrf_del_slave(vrf_dev, dev, false); } out: return NOTIFY_DONE; diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 3bd57527b1be..809f21fb93f5 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -740,6 +740,8 @@ static int uhdlc_open(struct net_device *dev) static void uhdlc_memclean(struct ucc_hdlc_private *priv) { + int i; + qe_muram_free(ioread16be(&priv->ucc_pram->riptr)); qe_muram_free(ioread16be(&priv->ucc_pram->tiptr)); @@ -770,14 +772,14 @@ static void uhdlc_memclean(struct ucc_hdlc_private *priv) kfree(priv->rx_skbuff); priv->rx_skbuff = NULL; + for (i = 0; i < TX_BD_RING_LEN; i++) { + dev_kfree_skb(priv->tx_skbuff[i]); + priv->tx_skbuff[i] = NULL; + } + kfree(priv->tx_skbuff); priv->tx_skbuff = NULL; - if (priv->uf_regs) { - iounmap(priv->uf_regs); - priv->uf_regs = NULL; - } - if (priv->uccf) { ucc_fast_free(priv->uccf); priv->uccf = NULL; @@ -1255,12 +1257,12 @@ static void ucc_hdlc_remove(struct platform_device *pdev) uhdlc_memclean(priv); - if (priv->utdm->si_regs) { + if (priv->utdm && priv->utdm->si_regs) { iounmap(priv->utdm->si_regs); priv->utdm->si_regs = NULL; } - if (priv->utdm->siram) { + if (priv->utdm && priv->utdm->siram) { iounmap(priv->utdm->siram); priv->utdm->siram = NULL; } diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 876aed765833..efb9f022d8c6 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -46,6 +46,7 @@ config ATH10K_SNOC depends on ARCH_QCOM || COMPILE_TEST depends on QCOM_SMEM depends on QCOM_RPROC_COMMON || QCOM_RPROC_COMMON=n + select POWER_SEQUENCING select QCOM_SCM select QCOM_QMI_HELPERS help diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 2519e2400d58..980a12fb2c6e 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -1838,10 +1838,22 @@ static struct ath12k_hw_group *ath12k_core_hw_group_alloc(struct ath12k_base *ab return ag; } +static void ath12k_core_free_wsi_info(struct ath12k_hw_group *ag) +{ + int i; + + for (i = 0; i < ag->num_devices; i++) { + of_node_put(ag->wsi_node[i]); + ag->wsi_node[i] = NULL; + } + ag->num_devices = 0; +} + static void ath12k_core_hw_group_free(struct ath12k_hw_group *ag) { mutex_lock(&ath12k_hw_group_mutex); + ath12k_core_free_wsi_info(ag); list_del(&ag->list); kfree(ag); @@ -1867,52 +1879,59 @@ static struct ath12k_hw_group *ath12k_core_hw_group_find_by_dt(struct ath12k_bas static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag, struct ath12k_base *ab) { - struct device_node *wsi_dev = ab->dev->of_node, *next_wsi_dev; - struct device_node *tx_endpoint, *next_rx_endpoint; - int device_count = 0; - - next_wsi_dev = wsi_dev; + struct device_node *next_wsi_dev; + int device_count = 0, ret = 0; + struct device_node *wsi_dev; - if (!next_wsi_dev) + wsi_dev = of_node_get(ab->dev->of_node); + if (!wsi_dev) return -ENODEV; do { - ag->wsi_node[device_count] = next_wsi_dev; + if (device_count >= ATH12K_MAX_DEVICES) { + ath12k_warn(ab, "device count in DT %d is more than limit %d\n", + device_count, ATH12K_MAX_DEVICES); + ret = -EINVAL; + break; + } + + ag->wsi_node[device_count++] = of_node_get(wsi_dev); - tx_endpoint = of_graph_get_endpoint_by_regs(next_wsi_dev, 0, -1); + struct device_node *tx_endpoint __free(device_node) = + of_graph_get_endpoint_by_regs(wsi_dev, 0, -1); if (!tx_endpoint) { - of_node_put(next_wsi_dev); - return -ENODEV; + ret = -ENODEV; + break; } - next_rx_endpoint = of_graph_get_remote_endpoint(tx_endpoint); + struct device_node *next_rx_endpoint __free(device_node) = + of_graph_get_remote_endpoint(tx_endpoint); if (!next_rx_endpoint) { - of_node_put(next_wsi_dev); - of_node_put(tx_endpoint); - return -ENODEV; + ret = -ENODEV; + break; } - of_node_put(tx_endpoint); - of_node_put(next_wsi_dev); - next_wsi_dev = of_graph_get_port_parent(next_rx_endpoint); if (!next_wsi_dev) { - of_node_put(next_rx_endpoint); - return -ENODEV; + ret = -ENODEV; + break; } - of_node_put(next_rx_endpoint); + of_node_put(wsi_dev); + wsi_dev = next_wsi_dev; + } while (ab->dev->of_node != wsi_dev); - device_count++; - if (device_count > ATH12K_MAX_DEVICES) { - ath12k_warn(ab, "device count in DT %d is more than limit %d\n", - device_count, ATH12K_MAX_DEVICES); - of_node_put(next_wsi_dev); - return -EINVAL; + if (ret) { + while (--device_count >= 0) { + of_node_put(ag->wsi_node[device_count]); + ag->wsi_node[device_count] = NULL; } - } while (wsi_dev != next_wsi_dev); - of_node_put(next_wsi_dev); + of_node_put(wsi_dev); + return ret; + } + + of_node_put(wsi_dev); ag->num_devices = device_count; return 0; @@ -1983,9 +2002,9 @@ static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *a ath12k_core_get_wsi_index(ag, ab)) { ath12k_dbg(ab, ATH12K_DBG_BOOT, "unable to get wsi info from dt, grouping single device"); + ath12k_core_free_wsi_info(ag); ag->id = ATH12K_INVALID_GROUP_ID; ag->num_devices = 1; - memset(ag->wsi_node, 0, sizeof(ag->wsi_node)); wsi->index = 0; } diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 250459facff3..b108ccd0f637 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -565,6 +565,9 @@ static int ath12k_dp_prepare_reo_update_elem(struct ath12k_dp *dp, lockdep_assert_held(&dp->dp_lock); + if (!peer->primary_link) + return 0; + elem = kzalloc_obj(*elem, GFP_ATOMIC); if (!elem) return -ENOMEM; @@ -1337,7 +1340,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc bool is_mcbc = rxcb->is_mcbc; bool is_eapol = rxcb->is_eapol; - peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rx_info->peer_id); + peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id); pubsta = peer ? peer->sta : NULL; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index fbdfe6424fd7..df2334f3bad6 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -788,7 +788,7 @@ struct ath12k_link_vif *ath12k_mac_get_arvif(struct ath12k *ar, u32 vdev_id) /* To use the arvif returned, caller must have held rcu read lock. */ - WARN_ON(!rcu_read_lock_any_held()); + lockdep_assert_in_rcu_read_lock(); arvif_iter.vdev_id = vdev_id; arvif_iter.ar = ar; diff --git a/drivers/net/wireless/ath/ath12k/p2p.c b/drivers/net/wireless/ath/ath12k/p2p.c index 59589748f1a8..19ebcd1d8eb2 100644 --- a/drivers/net/wireless/ath/ath12k/p2p.c +++ b/drivers/net/wireless/ath/ath12k/p2p.c @@ -123,7 +123,7 @@ static void ath12k_p2p_noa_update_vdev_iter(void *data, u8 *mac, struct ath12k_p2p_noa_arg *arg = data; struct ath12k_link_vif *arvif; - WARN_ON(!rcu_read_lock_any_held()); + lockdep_assert_in_rcu_read_lock(); arvif = &ahvif->deflink; if (!arvif->is_created || arvif->ar != arg->ar || arvif->vdev_id != arg->vdev_id) return; diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index 65a05a9520ff..b5e904a55aea 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -9778,7 +9778,7 @@ static void ath12k_wmi_rssi_dbm_conversion_params_info_event(struct ath12k_base *ab, struct sk_buff *skb) { - struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info; + struct ath12k_wmi_rssi_dbm_conv_info_arg rssi_info = {}; struct ath12k *ar; s32 noise_floor; u32 pdev_id; @@ -10251,7 +10251,7 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a { struct wmi_hw_data_filter_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10275,7 +10275,13 @@ int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_a "wmi hw data filter enable %d filter_bitmap 0x%x\n", arg->enable, arg->hw_filter_bitmap); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) @@ -10283,6 +10289,7 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) struct wmi_wow_host_wakeup_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10295,14 +10302,20 @@ int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow host wakeup ind\n"); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_enable(struct ath12k *ar) { struct wmi_wow_enable_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10317,7 +10330,13 @@ int ath12k_wmi_wow_enable(struct ath12k *ar) cmd->pause_iface_config = cpu_to_le32(WOW_IFACE_PAUSE_ENABLED); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow enable\n"); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, @@ -10327,6 +10346,7 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, struct wmi_wow_add_del_event_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10343,7 +10363,13 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow add wakeup event %s enable %d vdev_id %d\n", wow_wakeup_event(event), enable, vdev_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, @@ -10356,6 +10382,7 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, struct sk_buff *skb; void *ptr; size_t len; + int ret; len = sizeof(*cmd) + sizeof(*tlv) + /* array struct */ @@ -10435,7 +10462,13 @@ int ath12k_wmi_wow_add_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id, ath12k_dbg_dump(ar->ab, ATH12K_DBG_WMI, NULL, "wow bitmask: ", bitmap->bitmaskbuf, pattern_len); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) @@ -10443,6 +10476,7 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) struct wmi_wow_del_pattern_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10459,7 +10493,13 @@ int ath12k_wmi_wow_del_pattern(struct ath12k *ar, u32 vdev_id, u32 pattern_id) ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi tlv wow del pattern vdev_id %d pattern_id %d\n", vdev_id, pattern_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } static struct sk_buff * @@ -10595,6 +10635,7 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, struct wmi_pno_scan_req_arg *pno_scan) { struct sk_buff *skb; + int ret; if (pno_scan->enable) skb = ath12k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan); @@ -10604,7 +10645,13 @@ int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, if (IS_ERR_OR_NULL(skb)) return -ENOMEM; - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } static void ath12k_wmi_fill_ns_offload(struct ath12k *ar, @@ -10717,6 +10764,7 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, void *buf_ptr; size_t len; u8 ns_cnt, ns_ext_tuples = 0; + int ret; ns_cnt = offload->ipv6_count; @@ -10752,7 +10800,13 @@ int ath12k_wmi_arp_ns_offload(struct ath12k *ar, if (ns_ext_tuples) ath12k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, @@ -10762,7 +10816,7 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, struct wmi_gtk_rekey_offload_cmd *cmd; struct sk_buff *skb; __le64 replay_ctr; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10789,7 +10843,13 @@ int ath12k_wmi_gtk_rekey_offload(struct ath12k *ar, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "offload gtk rekey vdev: %d %d\n", arvif->vdev_id, enable); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, @@ -10797,7 +10857,7 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, { struct wmi_gtk_rekey_offload_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -10811,7 +10871,13 @@ int ath12k_wmi_gtk_rekey_getinfo(struct ath12k *ar, ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "get gtk rekey vdev_id: %d\n", arvif->vdev_id); - return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + ret = ath12k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_sta_keepalive(struct ath12k *ar, @@ -10822,6 +10888,7 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, struct wmi_sta_keepalive_cmd *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd) + sizeof(*arp); skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); @@ -10849,7 +10916,13 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, "wmi sta keepalive vdev %d enabled %d method %d interval %d\n", arg->vdev_id, arg->enabled, arg->method, arg->interval); - return ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params) diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 05c9c07591fc..6ca31d4ea437 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1738,7 +1738,8 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, } info->status.rates[ts->ts_final_idx].count = ts->ts_final_retry; - info->status.rates[ts->ts_final_idx + 1].idx = -1; + if (ts->ts_final_idx + 1 < IEEE80211_TX_MAX_RATES) + info->status.rates[ts->ts_final_idx + 1].idx = -1; if (unlikely(ts->ts_status)) { ah->stats.ack_fail++; diff --git a/drivers/net/wireless/broadcom/b43/xmit.c b/drivers/net/wireless/broadcom/b43/xmit.c index 7651b1bdb592..f0b082596637 100644 --- a/drivers/net/wireless/broadcom/b43/xmit.c +++ b/drivers/net/wireless/broadcom/b43/xmit.c @@ -702,7 +702,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) * key index, but the ucode passed it slightly different. */ keyidx = b43_kidx_to_raw(dev, keyidx); - B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key)); + if (B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key))) + goto drop; if (dev->key[keyidx].algorithm != B43_SEC_ALGO_NONE) { wlhdr_len = ieee80211_hdrlen(fctl); diff --git a/drivers/net/wireless/broadcom/b43legacy/xmit.c b/drivers/net/wireless/broadcom/b43legacy/xmit.c index efd63f4ce74f..ee199d4eaf03 100644 --- a/drivers/net/wireless/broadcom/b43legacy/xmit.c +++ b/drivers/net/wireless/broadcom/b43legacy/xmit.c @@ -476,7 +476,8 @@ void b43legacy_rx(struct b43legacy_wldev *dev, * key index, but the ucode passed it slightly different. */ keyidx = b43legacy_kidx_to_raw(dev, keyidx); - B43legacy_WARN_ON(keyidx >= dev->max_nr_keys); + if (B43legacy_WARN_ON(keyidx >= dev->max_nr_keys)) + goto drop; if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) { /* Remove PROTECTED flag to mark it as decrypted. */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 30f6fcb68632..8fb595733b9c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -2476,8 +2476,9 @@ static void brcmf_sdio_bus_stop(struct device *dev) brcmf_dbg(TRACE, "Enter\n"); if (bus->watchdog_tsk) { + get_task_struct(bus->watchdog_tsk); send_sig(SIGTERM, bus->watchdog_tsk, 1); - kthread_stop(bus->watchdog_tsk); + kthread_stop_put(bus->watchdog_tsk); bus->watchdog_tsk = NULL; } @@ -4567,8 +4568,9 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) if (bus) { /* Stop watchdog task */ if (bus->watchdog_tsk) { + get_task_struct(bus->watchdog_tsk); send_sig(SIGTERM, bus->watchdog_tsk, 1); - kthread_stop(bus->watchdog_tsk); + kthread_stop_put(bus->watchdog_tsk); bus->watchdog_tsk = NULL; } diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c index 4fae0e335136..5cc0c5cac257 100644 --- a/drivers/net/wireless/marvell/libertas/if_usb.c +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -310,6 +310,7 @@ static void if_usb_disconnect(struct usb_interface *intf) struct lbs_private *priv = cardp->priv; cardp->surprise_removed = 1; + wake_up(&cardp->fw_wq); if (priv) { lbs_stop_card(priv); @@ -633,9 +634,10 @@ static inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, unsigned long flags; u8 i; - if (recvlength > LBS_CMD_BUFFER_SIZE) { + if (recvlength < MESSAGE_HEADER_LEN || + recvlength > LBS_CMD_BUFFER_SIZE) { lbs_deb_usbd(&cardp->udev->dev, - "The receive buffer is too large\n"); + "The receive buffer is invalid: %d\n", recvlength); kfree_skb(skb); return; } diff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 591602beeec6..3cdf9ded876d 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c index 84eb15d729c7..120f0379f81d 100644 --- a/drivers/net/wireless/st/cw1200/pm.c +++ b/drivers/net/wireless/st/cw1200/pm.c @@ -264,14 +264,12 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) wiphy_err(priv->hw->wiphy, "PM request failed: %d. WoW is disabled.\n", ret); cw1200_wow_resume(hw); - mutex_unlock(&priv->conf_mutex); return -EBUSY; } /* Force resume if event is coming from the device. */ if (atomic_read(&priv->bh_rx)) { cw1200_wow_resume(hw); - mutex_unlock(&priv->conf_mutex); return -EAGAIN; } diff --git a/drivers/net/wwan/t7xx/t7xx_modem_ops.c b/drivers/net/wwan/t7xx/t7xx_modem_ops.c index 7968e208dd37..adb29d30c63f 100644 --- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c +++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c @@ -457,8 +457,20 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf offset = sizeof(struct feature_query); for (i = 0; i < FEATURE_COUNT && offset < data_length; i++) { + size_t remaining = data_length - offset; + size_t feat_data_len, feat_total; + + if (remaining < sizeof(*rt_feature)) + break; + rt_feature = data + offset; - offset += sizeof(*rt_feature) + le32_to_cpu(rt_feature->data_len); + feat_data_len = le32_to_cpu(rt_feature->data_len); + + if (feat_data_len > remaining - sizeof(*rt_feature)) + break; + + feat_total = sizeof(*rt_feature) + feat_data_len; + offset += feat_total; ft_spt_cfg = FIELD_GET(FEATURE_MSK, core->feature_set[i]); if (ft_spt_cfg != MTK_FEATURE_MUST_BE_SUPPORTED) @@ -468,8 +480,10 @@ static int t7xx_parse_host_rt_data(struct t7xx_fsm_ctl *ctl, struct t7xx_sys_inf if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED) return -EINVAL; - if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) - t7xx_port_enum_msg_handler(ctl->md, rt_feature->data); + if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM) { + t7xx_port_enum_msg_handler(ctl->md, rt_feature->data, + feat_data_len); + } } return 0; diff --git a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c index ae632ef96698..f869e4ed9ee9 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c +++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c @@ -117,6 +117,7 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c * t7xx_port_enum_msg_handler() - Parse the port enumeration message to create/remove nodes. * @md: Modem context. * @msg: Message. + * @msg_len: Length of @msg in bytes. * * Used to control create/remove device node. * @@ -124,12 +125,18 @@ static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *c * * 0 - Success. * * -EFAULT - Message check failure. */ -int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg) +int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len) { struct device *dev = &md->t7xx_dev->pdev->dev; unsigned int version, port_count, i; struct port_msg *port_msg = msg; + if (msg_len < sizeof(*port_msg)) { + dev_err(dev, "Port enum msg too short for header: need %zu, have %zu\n", + sizeof(*port_msg), msg_len); + return -EINVAL; + } + version = FIELD_GET(PORT_MSG_VERSION, le32_to_cpu(port_msg->info)); if (version != PORT_ENUM_VER || le32_to_cpu(port_msg->head_pattern) != PORT_ENUM_HEAD_PATTERN || @@ -141,6 +148,13 @@ int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg) } port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info)); + + if (msg_len < struct_size(port_msg, data, port_count)) { + dev_err(dev, "Port enum msg too short: need %zu, have %zu\n", + struct_size(port_msg, data, port_count), msg_len); + return -EINVAL; + } + for (i = 0; i < port_count; i++) { u32 port_info = le32_to_cpu(port_msg->data[i]); unsigned int ch_id; @@ -191,7 +205,7 @@ static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb) case CTL_ID_PORT_ENUM: skb_pull(skb, sizeof(*ctrl_msg_h)); - ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data); + ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data, skb->len); if (!ret) ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0); else diff --git a/drivers/net/wwan/t7xx/t7xx_port_proxy.h b/drivers/net/wwan/t7xx/t7xx_port_proxy.h index f0918b36e899..7c3190bf0fcf 100644 --- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h +++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h @@ -103,7 +103,7 @@ void t7xx_port_proxy_reset(struct port_proxy *port_prox); void t7xx_port_proxy_uninit(struct port_proxy *port_prox); int t7xx_port_proxy_init(struct t7xx_modem *md); void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state); -int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg); +int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg, size_t msg_len); int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id, bool en_flag); void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id); |
