--- /dev/null
+The Gianfar Ethernet Driver
+Sysfs File description
+
+Author: Andy Fleming <afleming@freescale.com>
+Updated: 2005-07-28
+
+SYSFS
+
+Several of the features of the gianfar driver are controlled
+through sysfs files. These are:
+
+bd_stash:
+To stash RX Buffer Descriptors in the L2, echo 'on' or '1' to
+bd_stash, echo 'off' or '0' to disable
+
+rx_stash_len:
+To stash the first n bytes of the packet in L2, echo the number
+of bytes to buf_stash_len. echo 0 to disable.
+
+WARNING: You could really screw these up if you set them too low or high!
+fifo_threshold:
+To change the number of bytes the controller needs in the
+fifo before it starts transmission, echo the number of bytes to
+fifo_thresh. Range should be 0-511.
+
+fifo_starve:
+When the FIFO has less than this many bytes during a transmit, it
+enters starve mode, and increases the priority of TX memory
+transactions. To change, echo the number of bytes to
+fifo_starve. Range should be 0-511.
+
+fifo_starve_off:
+Once in starve mode, the FIFO remains there until it has this
+many bytes. To change, echo the number of bytes to
+fifo_starve_off. Range should be 0-511.
+
+CHECKSUM OFFLOADING
+
+The eTSEC controller (first included in parts from late 2005 like
+the 8548) has the ability to perform TCP, UDP, and IP checksums
+in hardware. The Linux kernel only offloads the TCP and UDP
+checksums (and always performs the pseudo header checksums), so
+the driver only supports checksumming for TCP/IP and UDP/IP
+packets. Use ethtool to enable or disable this feature for RX
+and TX.
+
+VLAN
+
+In order to use VLAN, please consult Linux documentation on
+configuring VLANs. The gianfar driver supports hardware insertion and
+extraction of VLAN headers, but not filtering. Filtering will be
+done by the kernel.
+
+MULTICASTING
+
+The gianfar driver supports using the group hash table on the
+TSEC (and the extended hash table on the eTSEC) for multicast
+filtering. On the eTSEC, the exact-match MAC registers are used
+before the hash tables. See Linux documentation on how to join
+multicast groups.
+
+PADDING
+
+The gianfar driver supports padding received frames with 2 bytes
+to align the IP header to a 16-byte boundary, when supported by
+hardware.
+
+ETHTOOL
+
+The gianfar driver supports the use of ethtool for many
+configuration options. You must run ethtool only on currently
+open interfaces. See ethtool documentation for details.
dma_addr_t tx_bufs_dma;
signed char phys[4]; /* MII device addresses. */
char twistie, twist_row, twist_col; /* Twister tune state. */
- unsigned int default_port:4; /* Last dev->if_port value. */
+ unsigned int default_port : 4; /* Last dev->if_port value. */
+ unsigned int have_thread : 1;
spinlock_t lock;
spinlock_t rx_lock;
chip_t chipset;
- pid_t thr_pid;
- wait_queue_head_t thr_wait;
- struct completion thr_exited;
u32 rx_config;
struct rtl_extra_stats xstats;
- int time_to_die;
+
+ struct work_struct thread;
+
struct mii_if_info mii;
unsigned int regs_len;
unsigned long fifo_copy_timeout;
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 val);
-static void rtl8139_start_thread(struct net_device *dev);
+static void rtl8139_start_thread(struct rtl8139_private *tp);
static void rtl8139_tx_timeout (struct net_device *dev);
static void rtl8139_init_ring (struct net_device *dev);
static int rtl8139_start_xmit (struct sk_buff *skb,
static void rtl8139_set_rx_mode (struct net_device *dev);
static void __set_rx_mode (struct net_device *dev);
static void rtl8139_hw_start (struct net_device *dev);
+static void rtl8139_thread (void *_data);
static struct ethtool_ops rtl8139_ethtool_ops;
/* write MMIO register, with flush */
(debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1));
spin_lock_init (&tp->lock);
spin_lock_init (&tp->rx_lock);
- init_waitqueue_head (&tp->thr_wait);
- init_completion (&tp->thr_exited);
+ INIT_WORK(&tp->thread, rtl8139_thread, dev);
tp->mii.dev = dev;
tp->mii.mdio_read = mdio_read;
tp->mii.mdio_write = mdio_write;
dev->irq, RTL_R8 (MediaStatus),
tp->mii.full_duplex ? "full" : "half");
- rtl8139_start_thread(dev);
+ rtl8139_start_thread(tp);
return 0;
}
RTL_R8 (Config1));
}
-static int rtl8139_thread (void *data)
+static void rtl8139_thread (void *_data)
{
- struct net_device *dev = data;
+ struct net_device *dev = _data;
struct rtl8139_private *tp = netdev_priv(dev);
- unsigned long timeout;
-
- daemonize("%s", dev->name);
- allow_signal(SIGTERM);
-
- while (1) {
- timeout = next_tick;
- do {
- timeout = interruptible_sleep_on_timeout (&tp->thr_wait, timeout);
- /* make swsusp happy with our thread */
- try_to_freeze();
- } while (!signal_pending (current) && (timeout > 0));
-
- if (signal_pending (current)) {
- flush_signals(current);
- }
+ unsigned long thr_delay;
- if (tp->time_to_die)
- break;
-
- if (rtnl_lock_interruptible ())
- break;
+ if (rtnl_shlock_nowait() == 0) {
rtl8139_thread_iter (dev, tp, tp->mmio_addr);
rtnl_unlock ();
+
+ thr_delay = next_tick;
+ } else {
+ /* unlikely race. mitigate with fast poll. */
+ thr_delay = HZ / 2;
}
- complete_and_exit (&tp->thr_exited, 0);
+ schedule_delayed_work(&tp->thread, thr_delay);
}
-static void rtl8139_start_thread(struct net_device *dev)
+static void rtl8139_start_thread(struct rtl8139_private *tp)
{
- struct rtl8139_private *tp = netdev_priv(dev);
-
- tp->thr_pid = -1;
tp->twistie = 0;
- tp->time_to_die = 0;
if (tp->chipset == CH_8139_K)
tp->twistie = 1;
else if (tp->drv_flags & HAS_LNK_CHNG)
return;
- tp->thr_pid = kernel_thread(rtl8139_thread, dev, CLONE_FS|CLONE_FILES);
- if (tp->thr_pid < 0) {
- printk (KERN_WARNING "%s: unable to start kernel thread\n",
- dev->name);
+ tp->have_thread = 1;
+
+ schedule_delayed_work(&tp->thread, next_tick);
+}
+
+static void rtl8139_stop_thread(struct rtl8139_private *tp)
+{
+ if (tp->have_thread) {
+ cancel_rearming_delayed_work(&tp->thread);
+ tp->have_thread = 0;
}
}
{
struct rtl8139_private *tp = netdev_priv(dev);
void __iomem *ioaddr = tp->mmio_addr;
- int ret = 0;
unsigned long flags;
netif_stop_queue (dev);
- if (tp->thr_pid >= 0) {
- tp->time_to_die = 1;
- wmb();
- ret = kill_proc (tp->thr_pid, SIGTERM, 1);
- if (ret) {
- printk (KERN_ERR "%s: unable to signal thread\n", dev->name);
- return ret;
- }
- wait_for_completion (&tp->thr_exited);
- }
-
+ rtl8139_stop_thread(tp);
+
if (netif_msg_ifdown(tp))
printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n",
dev->name, RTL_R16 (IntrStatus));
If in doubt, say N.
+source "drivers/net/ixp2000/Kconfig"
+
config MYRI_SBUS
tristate "MyriCOM Gigabit Ethernet support"
depends on SBUS
It does not support the link failover and network management
features that "portable" vendor supplied sk98lin driver does.
-
+
+
+config SKY2
+ tristate "SysKonnect Yukon2 support (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ select CRC32
+ ---help---
+ This driver support the Marvell Yukon 2 Gigabit Ethernet adapter.
+
+ To compile this driver as a module, choose M here: the module
+ will be called sky2. This is recommended.
+
+config SKY2_EC_A1
+ bool "Support old Yukon-EC A1 chipset"
+ depends on SKY2
+ ---help---
+ Include support for early revisions of the Yukon EC chipset
+ that required extra workarounds. If in doubt, say N.
+
config SK98LIN
tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support"
depends on PCI
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
-gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_mii.o
+gianfar_driver-objs := gianfar.o \
+ gianfar_ethtool.o \
+ gianfar_mii.o \
+ gianfar_sysfs.o
#
# link order important here
obj-$(CONFIG_SPIDER_NET) += spidernet.o
obj-$(CONFIG_TC35815) += tc35815.o
obj-$(CONFIG_SKGE) += skge.o
+obj-$(CONFIG_SKY2) += sky2.o
obj-$(CONFIG_SK98LIN) += sk98lin/
obj-$(CONFIG_SKFP) += skfp/
obj-$(CONFIG_VIA_RHINE) += via-rhine.o
obj-$(CONFIG_HAMRADIO) += hamradio/
obj-$(CONFIG_IRDA) += irda/
obj-$(CONFIG_ETRAX_ETHERNET) += cris/
+obj-$(CONFIG_ENP2611_MSF_NET) += ixp2000/
obj-$(CONFIG_NETCONSOLE) += netconsole.o
obj-$(CONFIG_BONDING) += bonding.o
-bonding-objs := bond_main.o bond_3ad.o bond_alb.o
+bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o
// detect loopback situation
if (!MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->actor_system))) {
// INFO_RECEIVED_LOOPBACK_FRAMES
- printk(KERN_ERR DRV_NAME ": An illegal loopback occurred on adapter (%s)\n",
- port->slave->dev->name);
- printk(KERN_ERR "Check the configuration to verify that all Adapters "
- "are connected to 802.3ad compliant switch ports\n");
+ printk(KERN_ERR DRV_NAME ": %s: An illegal loopback occurred on "
+ "adapter (%s). Check the configuration to verify that all "
+ "Adapters are connected to 802.3ad compliant switch ports\n",
+ port->slave->dev->master->name, port->slave->dev->name);
__release_rx_machine_lock(port);
return;
}
}
}
if (!curr_port) { // meaning: the port was related to an aggregator but was not on the aggregator port list
- printk(KERN_WARNING DRV_NAME ": Warning: Port %d (on %s) was "
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: Port %d (on %s) was "
"related to aggregator %d but was not on its port list\n",
+ port->slave->dev->master->name,
port->actor_port_number, port->slave->dev->name,
port->aggregator->aggregator_identifier);
}
dprintk("Port %d joined LAG %d(new LAG)\n", port->actor_port_number, port->aggregator->aggregator_identifier);
} else {
- printk(KERN_ERR DRV_NAME ": Port %d (on %s) did not find a suitable aggregator\n",
+ printk(KERN_ERR DRV_NAME ": %s: Port %d (on %s) did not find a suitable aggregator\n",
+ port->slave->dev->master->name,
port->actor_port_number, port->slave->dev->name);
}
}
// check if any partner replys
if (best_aggregator->is_individual) {
- printk(KERN_WARNING DRV_NAME ": Warning: No 802.3ad response from the link partner "
- "for any adapters in the bond\n");
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: No 802.3ad response from "
+ "the link partner for any adapters in the bond\n",
+ best_aggregator->slave->dev->master->name);
}
// check if there are more than one aggregator
struct aggregator *aggregator;
if (bond == NULL) {
- printk(KERN_ERR "The slave %s is not attached to its bond\n", slave->dev->name);
+ printk(KERN_ERR DRV_NAME ": %s: The slave %s is not attached to its bond\n",
+ slave->dev->master->name, slave->dev->name);
return -1;
}
// if slave is null, the whole port is not initialized
if (!port->slave) {
- printk(KERN_WARNING DRV_NAME ": Trying to unbind an uninitialized port on %s\n", slave->dev->name);
+ printk(KERN_WARNING DRV_NAME ": Warning: %s: Trying to "
+ "unbind an uninitialized port on %s\n",
+ slave->dev->master->name, slave->dev->name);
return;
}
dprintk("Some port(s) related to LAG %d - replaceing with LAG %d\n", aggregator->aggregator_identifier, new_aggregator->aggregator_identifier);
if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) {
- printk(KERN_INFO DRV_NAME ": Removing an active aggregator\n");
+ printk(KERN_INFO DRV_NAME ": %s: Removing an active aggregator\n",
+ aggregator->slave->dev->master->name);
// select new active aggregator
select_new_active_agg = 1;
}
ad_agg_selection_logic(__get_first_agg(port));
}
} else {
- printk(KERN_WARNING DRV_NAME ": Warning: unbinding aggregator, "
- "and could not find a new aggregator for its ports\n");
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: unbinding aggregator, "
+ "and could not find a new aggregator for its ports\n",
+ slave->dev->master->name);
}
} else { // in case that the only port related to this aggregator is the one we want to remove
select_new_active_agg = aggregator->is_active;
// clear the aggregator
ad_clear_agg(aggregator);
if (select_new_active_agg) {
- printk(KERN_INFO "Removing an active aggregator\n");
+ printk(KERN_INFO DRV_NAME ": %s: Removing an active aggregator\n",
+ slave->dev->master->name);
// select new active aggregator
ad_agg_selection_logic(__get_first_agg(port));
}
// clear the aggregator
ad_clear_agg(temp_aggregator);
if (select_new_active_agg) {
- printk(KERN_INFO "Removing an active aggregator\n");
+ printk(KERN_INFO DRV_NAME ": %s: Removing an active aggregator\n",
+ slave->dev->master->name);
// select new active aggregator
ad_agg_selection_logic(__get_first_agg(port));
}
// select the active aggregator for the bond
if ((port = __get_first_port(bond))) {
if (!port->slave) {
- printk(KERN_WARNING DRV_NAME ": Warning: bond's first port is uninitialized\n");
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: bond's first port is "
+ "uninitialized\n", bond->dev->name);
goto re_arm;
}
// for each port run the state machines
for (port = __get_first_port(bond); port; port = __get_next_port(port)) {
if (!port->slave) {
- printk(KERN_WARNING DRV_NAME ": Warning: Found an uninitialized port\n");
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: Found an uninitialized "
+ "port\n", bond->dev->name);
goto re_arm;
}
port = &(SLAVE_AD_INFO(slave).port);
if (!port->slave) {
- printk(KERN_WARNING DRV_NAME ": Warning: port of slave %s is uninitialized\n", slave->dev->name);
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: port of slave %s is "
+ "uninitialized\n", slave->dev->name, slave->dev->master->name);
return;
}
// if slave is null, the whole port is not initialized
if (!port->slave) {
- printk(KERN_WARNING DRV_NAME ": Warning: speed changed for uninitialized port on %s\n",
- slave->dev->name);
+ printk(KERN_WARNING DRV_NAME ": Warning: %s: speed "
+ "changed for uninitialized port on %s\n",
+ slave->dev->master->name, slave->dev->name);
return;
}
// if slave is null, the whole port is not initialized
if (!port->slave) {
- printk(KERN_WARNING DRV_NAME ": Warning: duplex changed for uninitialized port on %s\n",
- slave->dev->name);
+ printk(KERN_WARNING DRV_NAME ": %s: Warning: duplex changed "
+ "for uninitialized port on %s\n",
+ slave->dev->master->name, slave->dev->name);
return;
}
// if slave is null, the whole port is not initialized
if (!port->slave) {
- printk(KERN_WARNING DRV_NAME ": Warning: link status changed for uninitialized port on %s\n",
- slave->dev->name);
+ printk(KERN_WARNING DRV_NAME ": Warning: %s: link status changed for "
+ "uninitialized port on %s\n",
+ slave->dev->master->name, slave->dev->name);
return;
}
}
if (bond_3ad_get_active_agg_info(bond, &ad_info)) {
- printk(KERN_DEBUG "ERROR: bond_3ad_get_active_agg_info failed\n");
+ printk(KERN_DEBUG DRV_NAME ": %s: Error: "
+ "bond_3ad_get_active_agg_info failed\n", dev->name);
goto out;
}
if (slaves_in_agg == 0) {
/*the aggregator is empty*/
- printk(KERN_DEBUG "ERROR: active aggregator is empty\n");
+ printk(KERN_DEBUG DRV_NAME ": %s: Error: active "
+ "aggregator is empty\n",
+ dev->name);
goto out;
}
}
if (slave_agg_no >= 0) {
- printk(KERN_ERR DRV_NAME ": Error: Couldn't find a slave to tx on for aggregator ID %d\n", agg_id);
+ printk(KERN_ERR DRV_NAME ": %s: Error: Couldn't find a slave to tx on "
+ "for aggregator ID %d\n", dev->name, agg_id);
goto out;
}
{
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
int size = TLB_HASH_TABLE_SIZE * sizeof(struct tlb_client_info);
+ struct tlb_client_info *new_hashtbl;
int i;
spin_lock_init(&(bond_info->tx_hashtbl_lock));
- _lock_tx_hashtbl(bond);
-
- bond_info->tx_hashtbl = kmalloc(size, GFP_KERNEL);
- if (!bond_info->tx_hashtbl) {
+ new_hashtbl = kmalloc(size, GFP_KERNEL);
+ if (!new_hashtbl) {
printk(KERN_ERR DRV_NAME
- ": Error: %s: Failed to allocate TLB hash table\n",
+ ": %s: Error: Failed to allocate TLB hash table\n",
bond->dev->name);
- _unlock_tx_hashtbl(bond);
return -1;
}
+ _lock_tx_hashtbl(bond);
+
+ bond_info->tx_hashtbl = new_hashtbl;
memset(bond_info->tx_hashtbl, 0, size);
client_info->mac_dst);
if (!skb) {
printk(KERN_ERR DRV_NAME
- ": Error: failed to create an ARP packet\n");
+ ": %s: Error: failed to create an ARP packet\n",
+ client_info->slave->dev->master->name);
continue;
}
skb = vlan_put_tag(skb, client_info->vlan_id);
if (!skb) {
printk(KERN_ERR DRV_NAME
- ": Error: failed to insert VLAN tag\n");
+ ": %s: Error: failed to insert VLAN tag\n",
+ client_info->slave->dev->master->name);
continue;
}
}
if (!client_info->slave) {
printk(KERN_ERR DRV_NAME
- ": Error: found a client with no channel in "
- "the client's hash table\n");
+ ": %s: Error: found a client with no channel in "
+ "the client's hash table\n",
+ bond->dev->name);
continue;
}
/*update all clients using this src_ip, that are not assigned
{
struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond));
struct packet_type *pk_type = &(BOND_ALB_INFO(bond).rlb_pkt_type);
+ struct rlb_client_info *new_hashtbl;
int size = RLB_HASH_TABLE_SIZE * sizeof(struct rlb_client_info);
int i;
spin_lock_init(&(bond_info->rx_hashtbl_lock));
- _lock_rx_hashtbl(bond);
-
- bond_info->rx_hashtbl = kmalloc(size, GFP_KERNEL);
- if (!bond_info->rx_hashtbl) {
+ new_hashtbl = kmalloc(size, GFP_KERNEL);
+ if (!new_hashtbl) {
printk(KERN_ERR DRV_NAME
- ": Error: %s: Failed to allocate RLB hash table\n",
+ ": %s: Error: Failed to allocate RLB hash table\n",
bond->dev->name);
- _unlock_rx_hashtbl(bond);
return -1;
}
+ _lock_rx_hashtbl(bond);
+
+ bond_info->rx_hashtbl = new_hashtbl;
bond_info->rx_hashtbl_head = RLB_NULL_INDEX;
skb = vlan_put_tag(skb, vlan->vlan_id);
if (!skb) {
printk(KERN_ERR DRV_NAME
- ": Error: failed to insert VLAN tag\n");
+ ": %s: Error: failed to insert VLAN tag\n",
+ bond->dev->name);
continue;
}
}
s_addr.sa_family = dev->type;
if (dev_set_mac_address(dev, &s_addr)) {
printk(KERN_ERR DRV_NAME
- ": Error: dev_set_mac_address of dev %s failed! ALB "
+ ": %s: Error: dev_set_mac_address of dev %s failed! ALB "
"mode requires that the base driver support setting "
"the hw address also when the network device's "
"interface is open\n",
- dev->name);
+ dev->master->name, dev->name);
return -EOPNOTSUPP;
}
return 0;
bond->alb_info.rlb_enabled);
printk(KERN_WARNING DRV_NAME
- ": Warning: the hw address of slave %s is in use by "
+ ": %s: Warning: the hw address of slave %s is in use by "
"the bond; giving it the hw address of %s\n",
- slave->dev->name, free_mac_slave->dev->name);
+ bond->dev->name, slave->dev->name, free_mac_slave->dev->name);
} else if (has_bond_addr) {
printk(KERN_ERR DRV_NAME
- ": Error: the hw address of slave %s is in use by the "
+ ": %s: Error: the hw address of slave %s is in use by the "
"bond; couldn't find a slave with a free hw address to "
"give it (this should not have happened)\n",
- slave->dev->name);
+ bond->dev->name, slave->dev->name);
return -EFAULT;
}
tlb_deinitialize(bond);
return res;
}
+ } else {
+ bond->alb_info.rlb_enabled = 0;
}
return 0;
read_lock(&bond->curr_slave_lock);
bond_for_each_slave(bond, slave, i) {
- alb_send_learning_packets(slave,slave->dev->dev_addr);
+ alb_send_learning_packets(slave, slave->dev->dev_addr);
}
read_unlock(&bond->curr_slave_lock);
* Set version to 2.6.3.
* 2005/09/26 - Jay Vosburgh <fubar@us.ibm.com>
* - Removed backwards compatibility for old ifenslaves. Version 2.6.4.
+ * 2005/09/27 - Mitch Williams <mitch.a.williams at intel dot com>
+ * - Radheka Godse <radheka.godse at intel dot com>
+ * - Split out bond creation code to allow for sysfs interface.
+ * - Removed static declaration on some functions and data items.
+ * - Added sysfs support, including capability to add/remove/change
+ * any bond at runtime.
+ *
+ * - Miscellaneous:
+ * - Added bonding: <bondname>: prefix to sysfs log messages
+ * - Added arp_ip_targets to /proc entry
+ * - Allow ARP target table to have empty entries
+ * - trivial fix: added missing modes description to modinfo
+ * - Corrected bug in ALB init where kmalloc is called inside
+ * a held lock
+ * - Corrected behavior to maintain bond link when changing
+ * from arp monitor to miimon and vice versa
+ * - Added missing bonding: <bondname>: prefix to alb, ad log messages
+ * - Fixed stack dump warnings seen if changing between miimon
+ * and arp monitoring when the bond interface is down.
+ * - Fixed stack dump warnings seen when enslaving an e100
+ * driver
+ * - Set version to 3.0.0
*/
//#define BONDING_DEBUG 1
static char *xmit_hash_policy = NULL;
static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[BOND_MAX_ARP_TARGETS] = { NULL, };
+struct bond_params bonding_defaults;
module_param(max_bonds, int, 0);
MODULE_PARM_DESC(max_bonds, "Max number of bonded devices");
module_param(updelay, int, 0);
MODULE_PARM_DESC(updelay, "Delay before considering link up, in milliseconds");
module_param(downdelay, int, 0);
-MODULE_PARM_DESC(downdelay, "Delay before considering link down, in milliseconds");
+MODULE_PARM_DESC(downdelay, "Delay before considering link down, "
+ "in milliseconds");
module_param(use_carrier, int, 0);
-MODULE_PARM_DESC(use_carrier, "Use netif_carrier_ok (vs MII ioctls) in miimon; 0 for off, 1 for on (default)");
+MODULE_PARM_DESC(use_carrier, "Use netif_carrier_ok (vs MII ioctls) in miimon; "
+ "0 for off, 1 for on (default)");
module_param(mode, charp, 0);
-MODULE_PARM_DESC(mode, "Mode of operation : 0 for round robin, 1 for active-backup, 2 for xor");
+MODULE_PARM_DESC(mode, "Mode of operation : 0 for balance-rr, "
+ "1 for active-backup, 2 for balance-xor, "
+ "3 for broadcast, 4 for 802.3ad, 5 for balance-tlb, "
+ "6 for balance-alb");
module_param(primary, charp, 0);
MODULE_PARM_DESC(primary, "Primary network device to use");
module_param(lacp_rate, charp, 0);
-MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner (slow/fast)");
+MODULE_PARM_DESC(lacp_rate, "LACPDU tx rate to request from 802.3ad partner "
+ "(slow/fast)");
module_param(xmit_hash_policy, charp, 0);
-MODULE_PARM_DESC(xmit_hash_policy, "XOR hashing method : 0 for layer 2 (default), 1 for layer 3+4");
+MODULE_PARM_DESC(xmit_hash_policy, "XOR hashing method: 0 for layer 2 (default)"
+ ", 1 for layer 3+4");
module_param(arp_interval, int, 0);
MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
module_param_array(arp_ip_target, charp, NULL, 0);
static const char *version =
DRV_DESCRIPTION ": v" DRV_VERSION " (" DRV_RELDATE ")\n";
-static LIST_HEAD(bond_dev_list);
+LIST_HEAD(bond_dev_list);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *bond_proc_dir = NULL;
#endif
+extern struct rw_semaphore bonding_rwsem;
static u32 arp_target[BOND_MAX_ARP_TARGETS] = { 0, } ;
static int arp_ip_count = 0;
static int bond_mode = BOND_MODE_ROUNDROBIN;
static int xmit_hashtype= BOND_XMIT_POLICY_LAYER2;
static int lacp_fast = 0;
-struct bond_parm_tbl {
- char *modename;
- int mode;
-};
-static struct bond_parm_tbl bond_lacp_tbl[] = {
+struct bond_parm_tbl bond_lacp_tbl[] = {
{ "slow", AD_LACP_SLOW},
{ "fast", AD_LACP_FAST},
{ NULL, -1},
};
-static struct bond_parm_tbl bond_mode_tbl[] = {
+struct bond_parm_tbl bond_mode_tbl[] = {
{ "balance-rr", BOND_MODE_ROUNDROBIN},
{ "active-backup", BOND_MODE_ACTIVEBACKUP},
{ "balance-xor", BOND_MODE_XOR},
{ NULL, -1},
};
-static struct bond_parm_tbl xmit_hashtype_tbl[] = {
+struct bond_parm_tbl xmit_hashtype_tbl[] = {
{ "layer2", BOND_XMIT_POLICY_LAYER2},
{ "layer3+4", BOND_XMIT_POLICY_LAYER34},
{ NULL, -1},
/*-------------------------- Forward declarations ---------------------------*/
-static inline void bond_set_mode_ops(struct bonding *bond, int mode);
static void bond_send_gratuitous_arp(struct bonding *bond);
/*---------------------------- General routines -----------------------------*/
-static const char *bond_mode_name(int mode)
+const char *bond_mode_name(int mode)
{
switch (mode) {
case BOND_MODE_ROUNDROBIN :
res = bond_add_vlan(bond, vid);
if (res) {
printk(KERN_ERR DRV_NAME
- ": %s: Failed to add vlan id %d\n",
+ ": %s: Error: Failed to add vlan id %d\n",
bond_dev->name, vid);
}
}
res = bond_del_vlan(bond, vid);
if (res) {
printk(KERN_ERR DRV_NAME
- ": %s: Failed to remove vlan id %d\n",
+ ": %s: Error: Failed to remove vlan id %d\n",
bond_dev->name, vid);
}
}
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
-static void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
+void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
{
struct slave *old_active = bond->curr_active_slave;
*
* Warning: Caller must hold curr_slave_lock for writing.
*/
-static void bond_select_active_slave(struct bonding *bond)
+void bond_select_active_slave(struct bonding *bond)
{
struct slave *best_slave;
/*---------------------------------- IOCTL ----------------------------------*/
-static int bond_sethwaddr(struct net_device *bond_dev, struct net_device *slave_dev)
+int bond_sethwaddr(struct net_device *bond_dev, struct net_device *slave_dev)
{
dprintk("bond_dev=%p\n", bond_dev);
dprintk("slave_dev=%p\n", slave_dev);
}
/* enslave device <slave> to bond device <master> */
-static int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
+int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
{
struct bonding *bond = bond_dev->priv;
struct slave *new_slave = NULL;
if (!bond->params.use_carrier && slave_dev->ethtool_ops == NULL &&
slave_dev->do_ioctl == NULL) {
printk(KERN_WARNING DRV_NAME
- ": Warning : no link monitoring support for %s\n",
- slave_dev->name);
+ ": %s: Warning: no link monitoring support for %s\n",
+ bond_dev->name, slave_dev->name);
}
/* bond must be initialized by bond_open() before enslaving */
dprintk("%s: NETIF_F_VLAN_CHALLENGED\n", slave_dev->name);
if (!list_empty(&bond->vlan_list)) {
printk(KERN_ERR DRV_NAME
- ": Error: cannot enslave VLAN "
+ ": %s: Error: cannot enslave VLAN "
"challenged slave %s on VLAN enabled "
- "bond %s\n", slave_dev->name,
+ "bond %s\n", bond_dev->name, slave_dev->name,
bond_dev->name);
return -EPERM;
} else {
printk(KERN_WARNING DRV_NAME
- ": Warning: enslaved VLAN challenged "
+ ": %s: Warning: enslaved VLAN challenged "
"slave %s. Adding VLANs will be blocked as "
"long as %s is part of bond %s\n",
- slave_dev->name, slave_dev->name,
+ bond_dev->name, slave_dev->name, slave_dev->name,
bond_dev->name);
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
}
if (slave_dev->set_mac_address == NULL) {
printk(KERN_ERR DRV_NAME
- ": Error: The slave device you specified does "
- "not support setting the MAC address.\n");
- printk(KERN_ERR
- "Your kernel likely does not support slave devices.\n");
-
- res = -EOPNOTSUPP;
+ ": %s: Error: The slave device you specified does "
+ "not support setting the MAC address. "
+ "Your kernel likely does not support slave "
+ "devices.\n", bond_dev->name);
+ res = -EOPNOTSUPP;
goto err_undo_flags;
}
* the messages for netif_carrier.
*/
printk(KERN_WARNING DRV_NAME
- ": Warning: MII and ETHTOOL support not "
+ ": %s: Warning: MII and ETHTOOL support not "
"available for interface %s, and "
"arp_interval/arp_ip_target module parameters "
"not specified, thus bonding will not detect "
"link failures! see bonding.txt for details.\n",
- slave_dev->name);
+ bond_dev->name, slave_dev->name);
} else if (link_reporting == -1) {
/* unable get link status using mii/ethtool */
printk(KERN_WARNING DRV_NAME
- ": Warning: can't get link status from "
+ ": %s: Warning: can't get link status from "
"interface %s; the network driver associated "
"with this interface does not support MII or "
"ETHTOOL link status reporting, thus miimon "
"has no effect on this interface.\n",
- slave_dev->name);
+ bond_dev->name, slave_dev->name);
}
}
if (bond_update_speed_duplex(new_slave) &&
(new_slave->link != BOND_LINK_DOWN)) {
printk(KERN_WARNING DRV_NAME
- ": Warning: failed to get speed and duplex from %s, "
+ ": %s: Warning: failed to get speed and duplex from %s, "
"assumed to be 100Mb/sec and Full.\n",
- new_slave->dev->name);
+ bond_dev->name, new_slave->dev->name);
if (bond->params.mode == BOND_MODE_8023AD) {
- printk(KERN_WARNING
- "Operation of 802.3ad mode requires ETHTOOL "
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Warning: Operation of 802.3ad mode requires ETHTOOL "
"support in base driver for proper aggregator "
- "selection.\n");
+ "selection.\n", bond_dev->name);
}
}
write_unlock_bh(&bond->lock);
+ res = bond_create_slave_symlinks(bond_dev, slave_dev);
+ if (res)
+ goto err_unset_master;
+
printk(KERN_INFO DRV_NAME
": %s: enslaving %s as a%s interface with a%s link.\n",
bond_dev->name, slave_dev->name,
* for Bonded connections:
* The first up interface should be left on and all others downed.
*/
-static int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
+int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
{
struct bonding *bond = bond_dev->priv;
struct slave *slave, *oldcurrent;
if (!(slave_dev->flags & IFF_SLAVE) ||
(slave_dev->master != bond_dev)) {
printk(KERN_ERR DRV_NAME
- ": Error: %s: cannot release %s.\n",
+ ": %s: Error: cannot release %s.\n",
bond_dev->name, slave_dev->name);
return -EINVAL;
}
ETH_ALEN);
if (!mac_addr_differ && (bond->slave_cnt > 1)) {
printk(KERN_WARNING DRV_NAME
- ": Warning: the permanent HWaddr of %s "
+ ": %s: Warning: the permanent HWaddr of %s "
"- %02X:%02X:%02X:%02X:%02X:%02X - is "
"still in use by %s. Set the HWaddr of "
"%s to a different address to avoid "
"conflicts.\n",
+ bond_dev->name,
slave_dev->name,
slave->perm_hwaddr[0],
slave->perm_hwaddr[1],
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
} else {
printk(KERN_WARNING DRV_NAME
- ": Warning: clearing HW address of %s while it "
+ ": %s: Warning: clearing HW address of %s while it "
"still has VLANs.\n",
- bond_dev->name);
+ bond_dev->name, bond_dev->name);
printk(KERN_WARNING DRV_NAME
- ": When re-adding slaves, make sure the bond's "
- "HW address matches its VLANs'.\n");
+ ": %s: When re-adding slaves, make sure the bond's "
+ "HW address matches its VLANs'.\n",
+ bond_dev->name);
}
} else if ((bond_dev->features & NETIF_F_VLAN_CHALLENGED) &&
!bond_has_challenged_slaves(bond)) {
printk(KERN_INFO DRV_NAME
- ": last VLAN challenged slave %s "
+ ": %s: last VLAN challenged slave %s "
"left bond %s. VLAN blocking is removed\n",
- slave_dev->name, bond_dev->name);
+ bond_dev->name, slave_dev->name, bond_dev->name);
bond_dev->features &= ~NETIF_F_VLAN_CHALLENGED;
}
write_unlock_bh(&bond->lock);
+ /* must do this from outside any spinlocks */
+ bond_destroy_slave_symlinks(bond_dev, slave_dev);
+
bond_del_vlans_from_slave(bond, slave_dev);
/* If the mode USES_PRIMARY, then we should only remove its
*/
write_unlock_bh(&bond->lock);
+ bond_destroy_slave_symlinks(bond_dev, slave_dev);
bond_del_vlans_from_slave(bond, slave_dev);
/* If the mode USES_PRIMARY, then we should only remove its
bond_dev->features |= NETIF_F_VLAN_CHALLENGED;
} else {
printk(KERN_WARNING DRV_NAME
- ": Warning: clearing HW address of %s while it "
+ ": %s: Warning: clearing HW address of %s while it "
"still has VLANs.\n",
- bond_dev->name);
+ bond_dev->name, bond_dev->name);
printk(KERN_WARNING DRV_NAME
- ": When re-adding slaves, make sure the bond's "
- "HW address matches its VLANs'.\n");
+ ": %s: When re-adding slaves, make sure the bond's "
+ "HW address matches its VLANs'.\n",
+ bond_dev->name);
}
printk(KERN_INFO DRV_NAME
/*-------------------------------- Monitoring -------------------------------*/
/* this function is called regularly to monitor each slave's link. */
-static void bond_mii_monitor(struct net_device *bond_dev)
+void bond_mii_monitor(struct net_device *bond_dev)
{
struct bonding *bond = bond_dev->priv;
struct slave *slave, *oldcurrent;
break;
default:
/* Should not happen */
- printk(KERN_ERR "bonding: Error: %s Illegal value (link=%d)\n",
- slave->dev->name, slave->link);
+ printk(KERN_ERR DRV_NAME
+ ": %s: Error: %s Illegal value (link=%d)\n",
+ bond_dev->name,
+ slave->dev->name,
+ slave->link);
goto out;
} /* end of switch (slave->link) */
struct flowi fl;
struct rtable *rt;
- for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
+ for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
+ if (!targets[i])
+ continue;
dprintk("basa: target %x\n", targets[i]);
if (list_empty(&bond->vlan_list)) {
dprintk("basa: empty vlan: arp_send\n");
* arp is transmitted to generate traffic. see activebackup_arp_monitor for
* arp monitoring in active backup mode.
*/
-static void bond_loadbalance_arp_mon(struct net_device *bond_dev)
+void bond_loadbalance_arp_mon(struct net_device *bond_dev)
{
struct bonding *bond = bond_dev->priv;
struct slave *slave, *oldcurrent;
* may have received.
* see loadbalance_arp_monitor for arp monitoring in load balancing mode
*/
-static void bond_activebackup_arp_mon(struct net_device *bond_dev)
+void bond_activebackup_arp_mon(struct net_device *bond_dev)
{
struct bonding *bond = bond_dev->priv;
struct slave *slave;
{
struct bonding *bond = seq->private;
struct slave *curr;
+ int i;
+ u32 target;
read_lock(&bond->curr_slave_lock);
curr = bond->curr_active_slave;
seq_printf(seq, "Bonding Mode: %s\n",
bond_mode_name(bond->params.mode));
+ if (bond->params.mode == BOND_MODE_XOR ||
+ bond->params.mode == BOND_MODE_8023AD) {
+ seq_printf(seq, "Transmit Hash Policy: %s (%d)\n",
+ xmit_hashtype_tbl[bond->params.xmit_policy].modename,
+ bond->params.xmit_policy);
+ }
+
if (USES_PRIMARY(bond->params.mode)) {
seq_printf(seq, "Primary Slave: %s\n",
- (bond->params.primary[0]) ?
- bond->params.primary : "None");
+ (bond->primary_slave) ?
+ bond->primary_slave->dev->name : "None");
seq_printf(seq, "Currently Active Slave: %s\n",
(curr) ? curr->dev->name : "None");
seq_printf(seq, "Down Delay (ms): %d\n",
bond->params.downdelay * bond->params.miimon);
+
+ /* ARP information */
+ if(bond->params.arp_interval > 0) {
+ int printed=0;
+ seq_printf(seq, "ARP Polling Interval (ms): %d\n",
+ bond->params.arp_interval);
+
+ seq_printf(seq, "ARP IP target/s (n.n.n.n form):");
+
+ for(i = 0; (i < BOND_MAX_ARP_TARGETS) ;i++) {
+ if (!bond->params.arp_targets[i])
+ continue;
+ if (printed)
+ seq_printf(seq, ",");
+ target = ntohl(bond->params.arp_targets[i]);
+ seq_printf(seq, " %d.%d.%d.%d", HIPQUAD(target));
+ printed = 1;
+ }
+ seq_printf(seq, "\n");
+ }
+
if (bond->params.mode == BOND_MODE_8023AD) {
struct ad_info ad_info;
bond_remove_proc_entry(bond);
bond_create_proc_entry(bond);
#endif
-
+ down_write(&(bonding_rwsem));
+ bond_destroy_sysfs_entry(bond);
+ bond_create_sysfs_entry(bond);
+ up_write(&(bonding_rwsem));
return NOTIFY_DONE;
}
return -EPERM;
}
+ down_write(&(bonding_rwsem));
slave_dev = dev_get_by_name(ifr->ifr_slave);
dprintk("slave_dev=%p: \n", slave_dev);
dev_put(slave_dev);
}
+ up_write(&(bonding_rwsem));
return res;
}
bond_for_each_slave(bond, slave, i) {
dprintk("s %p s->p %p c_m %p\n", slave,
slave->prev, slave->dev->change_mtu);
+
res = dev_set_mtu(slave->dev, new_mtu);
if (res) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2) {
printk(KERN_ERR DRV_NAME
- ": Error: bond_xmit_broadcast(): "
- "skb_clone() failed\n");
+ ": %s: Error: bond_xmit_broadcast(): "
+ "skb_clone() failed\n",
+ bond_dev->name);
continue;
}
/*
* set bond mode specific net device operations
*/
-static inline void bond_set_mode_ops(struct bonding *bond, int mode)
+void bond_set_mode_ops(struct bonding *bond, int mode)
{
struct net_device *bond_dev = bond->dev;
default:
/* Should never happen, mode already checked */
printk(KERN_ERR DRV_NAME
- ": Error: Unknown bonding mode %d\n",
+ ": %s: Error: Unknown bonding mode %d\n",
+ bond_dev->name,
mode);
break;
}
* Does not allocate but creates a /proc entry.
* Allowed to fail.
*/
-static int __init bond_init(struct net_device *bond_dev, struct bond_params *params)
+static int bond_init(struct net_device *bond_dev, struct bond_params *params)
{
struct bonding *bond = bond_dev->priv;
/* De-initialize device specific data.
* Caller must hold rtnl_lock.
*/
-static inline void bond_deinit(struct net_device *bond_dev)
+void bond_deinit(struct net_device *bond_dev)
{
struct bonding *bond = bond_dev->priv;
* Convert string input module parms. Accept either the
* number of the mode or its string name.
*/
-static inline int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl)
+int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl)
{
int i;
if (max_bonds < 1 || max_bonds > INT_MAX) {
printk(KERN_WARNING DRV_NAME
": Warning: max_bonds (%d) not in range %d-%d, so it "
- "was reset to BOND_DEFAULT_MAX_BONDS (%d)",
+ "was reset to BOND_DEFAULT_MAX_BONDS (%d)\n",
max_bonds, 1, INT_MAX, BOND_DEFAULT_MAX_BONDS);
max_bonds = BOND_DEFAULT_MAX_BONDS;
}
return 0;
}
+/* Create a new bond based on the specified name and bonding parameters.
+ * Caller must NOT hold rtnl_lock; we need to release it here before we
+ * set up our sysfs entries.
+ */
+int bond_create(char *name, struct bond_params *params, struct bonding **newbond)
+{
+ struct net_device *bond_dev;
+ int res;
+
+ rtnl_lock();
+ bond_dev = alloc_netdev(sizeof(struct bonding), name, ether_setup);
+ if (!bond_dev) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: eek! can't alloc netdev!\n",
+ name);
+ res = -ENOMEM;
+ goto out_rtnl;
+ }
+
+ /* bond_init() must be called after dev_alloc_name() (for the
+ * /proc files), but before register_netdevice(), because we
+ * need to set function pointers.
+ */
+
+ res = bond_init(bond_dev, params);
+ if (res < 0) {
+ goto out_netdev;
+ }
+
+ SET_MODULE_OWNER(bond_dev);
+
+ res = register_netdevice(bond_dev);
+ if (res < 0) {
+ goto out_bond;
+ }
+ if (newbond)
+ *newbond = bond_dev->priv;
+
+ rtnl_unlock(); /* allows sysfs registration of net device */
+ res = bond_create_sysfs_entry(bond_dev->priv);
+ goto done;
+out_bond:
+ bond_deinit(bond_dev);
+out_netdev:
+ free_netdev(bond_dev);
+out_rtnl:
+ rtnl_unlock();
+done:
+ return res;
+}
+
static int __init bonding_init(void)
{
- struct bond_params params;
int i;
int res;
+ char new_bond_name[8]; /* Enough room for 999 bonds at init. */
printk(KERN_INFO "%s", version);
- res = bond_check_params(¶ms);
+ res = bond_check_params(&bonding_defaults);
if (res) {
- return res;
+ goto out;
}
- rtnl_lock();
-
#ifdef CONFIG_PROC_FS
bond_create_proc_dir();
#endif
-
for (i = 0; i < max_bonds; i++) {
- struct net_device *bond_dev;
-
- bond_dev = alloc_netdev(sizeof(struct bonding), "", ether_setup);
- if (!bond_dev) {
- res = -ENOMEM;
- goto out_err;
- }
-
- res = dev_alloc_name(bond_dev, "bond%d");
- if (res < 0) {
- free_netdev(bond_dev);
- goto out_err;
- }
-
- /* bond_init() must be called after dev_alloc_name() (for the
- * /proc files), but before register_netdevice(), because we
- * need to set function pointers.
- */
- res = bond_init(bond_dev, ¶ms);
- if (res < 0) {
- free_netdev(bond_dev);
- goto out_err;
- }
-
- SET_MODULE_OWNER(bond_dev);
-
- res = register_netdevice(bond_dev);
- if (res < 0) {
- bond_deinit(bond_dev);
- free_netdev(bond_dev);
- goto out_err;
- }
+ sprintf(new_bond_name, "bond%d",i);
+ res = bond_create(new_bond_name,&bonding_defaults, NULL);
+ if (res)
+ goto err;
}
- rtnl_unlock();
+ res = bond_create_sysfs();
+ if (res)
+ goto err;
+
register_netdevice_notifier(&bond_netdev_notifier);
register_inetaddr_notifier(&bond_inetaddr_notifier);
- return 0;
-
-out_err:
- /*
- * rtnl_unlock() will run netdev_run_todo(), putting the
- * thus-far-registered bonding devices into a state which
- * unregigister_netdevice() will accept
- */
- rtnl_unlock();
+ goto out;
+err:
rtnl_lock();
-
- /* free and unregister all bonds that were successfully added */
bond_free_all();
-
+ bond_destroy_sysfs();
rtnl_unlock();
-
+out:
return res;
+
}
static void __exit bonding_exit(void)
rtnl_lock();
bond_free_all();
+ bond_destroy_sysfs();
rtnl_unlock();
}
--- /dev/null
+
+/*
+ * Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ *
+ * Changes:
+ *
+ * 2004/12/12 - Mitch Williams <mitch.a.williams at intel dot com>
+ * - Initial creation of sysfs interface.
+ *
+ * 2005/06/22 - Radheka Godse <radheka.godse at intel dot com>
+ * - Added ifenslave -c type functionality to sysfs
+ * - Added sysfs files for attributes such as MII Status and
+ * 802.3ad aggregator that are displayed in /proc
+ * - Added "name value" format to sysfs "mode" and
+ * "lacp_rate", for e.g., "active-backup 1" or "slow 0" for
+ * consistency and ease of script parsing
+ * - Fixed reversal of octets in arp_ip_targets via sysfs
+ * - sysfs support to handle bond interface re-naming
+ * - Moved all sysfs entries into /sys/class/net instead of
+ * of using a standalone subsystem.
+ * - Added sysfs symlinks between masters and slaves
+ * - Corrected bugs in sysfs unload path when creating bonds
+ * with existing interface names.
+ * - Removed redundant sysfs stat file since it duplicates slave info
+ * from the proc file
+ * - Fixed errors in sysfs show/store arp targets.
+ * - For consistency with ifenslave, instead of exiting
+ * with an error, updated bonding sysfs to
+ * close and attempt to enslave an up adapter.
+ * - Fixed NULL dereference when adding a slave interface
+ * that does not exist.
+ * - Added checks in sysfs bonding to reject invalid ip addresses
+ * - Synch up with post linux-2.6.12 bonding changes
+ * - Created sysfs bond attrib for xmit_hash_policy
+ *
+ * 2005/09/19 - Mitch Williams <mitch.a.williams at intel dot com>
+ * - Changed semantics of multi-item files to be command-based
+ * instead of list-based.
+ * - Changed ARP target handler to use in_aton instead of sscanf
+ * - Style changes.
+ * 2005/09/27 - Mitch Williams <mitch.a.williams at intel dot com>
+ * - Made line endings consistent.
+ * - Removed "none" from primary output - just put blank instead
+ * - Fixed bug with long interface names
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/in.h>
+#include <linux/sysfs.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/inet.h>
+#include <linux/rtnetlink.h>
+
+/* #define BONDING_DEBUG 1 */
+#include "bonding.h"
+#define to_class_dev(obj) container_of(obj,struct class_device,kobj)
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+#define to_bond(cd) ((struct bonding *)(to_net_dev(cd)->priv))
+
+/*---------------------------- Declarations -------------------------------*/
+
+
+extern struct list_head bond_dev_list;
+extern struct bond_params bonding_defaults;
+extern struct bond_parm_tbl bond_mode_tbl[];
+extern struct bond_parm_tbl bond_lacp_tbl[];
+extern struct bond_parm_tbl xmit_hashtype_tbl[];
+
+static int expected_refcount = -1;
+static struct class *netdev_class;
+/*--------------------------- Data Structures -----------------------------*/
+
+/* Bonding sysfs lock. Why can't we just use the subsytem lock?
+ * Because kobject_register tries to acquire the subsystem lock. If
+ * we already hold the lock (which we would if the user was creating
+ * a new bond through the sysfs interface), we deadlock.
+ * This lock is only needed when deleting a bond - we need to make sure
+ * that we don't collide with an ongoing ioctl.
+ */
+
+struct rw_semaphore bonding_rwsem;
+
+
+
+
+/*------------------------------ Functions --------------------------------*/
+
+/*
+ * "show" function for the bond_masters attribute.
+ * The class parameter is ignored.
+ */
+static ssize_t bonding_show_bonds(struct class *cls, char *buffer)
+{
+ int res = 0;
+ struct bonding *bond;
+
+ down_read(&(bonding_rwsem));
+
+ list_for_each_entry(bond, &bond_dev_list, bond_list) {
+ if (res > (PAGE_SIZE - IFNAMSIZ)) {
+ /* not enough space for another interface name */
+ if ((PAGE_SIZE - res) > 10)
+ res = PAGE_SIZE - 10;
+ res += sprintf(buffer + res, "++more++");
+ break;
+ }
+ res += sprintf(buffer + res, "%s ",
+ bond->dev->name);
+ }
+ res += sprintf(buffer + res, "\n");
+ res++;
+ up_read(&(bonding_rwsem));
+ return res;
+}
+
+/*
+ * "store" function for the bond_masters attribute. This is what
+ * creates and deletes entire bonds.
+ *
+ * The class parameter is ignored.
+ *
+ */
+
+static ssize_t bonding_store_bonds(struct class *cls, const char *buffer, size_t count)
+{
+ char command[IFNAMSIZ + 1] = {0, };
+ char *ifname;
+ int res = count;
+ struct bonding *bond;
+ struct bonding *nxt;
+
+ down_write(&(bonding_rwsem));
+ sscanf(buffer, "%16s", command); /* IFNAMSIZ*/
+ ifname = command + 1;
+ if ((strlen(command) <= 1) ||
+ !dev_valid_name(ifname))
+ goto err_no_cmd;
+
+ if (command[0] == '+') {
+
+ /* Check to see if the bond already exists. */
+ list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list)
+ if (strnicmp(bond->dev->name, ifname, IFNAMSIZ) == 0) {
+ printk(KERN_ERR DRV_NAME
+ ": cannot add bond %s; it already exists\n",
+ ifname);
+ res = -EPERM;
+ goto out;
+ }
+
+ printk(KERN_INFO DRV_NAME
+ ": %s is being created...\n", ifname);
+ if (bond_create(ifname, &bonding_defaults, &bond)) {
+ printk(KERN_INFO DRV_NAME
+ ": %s interface already exists. Bond creation failed.\n",
+ ifname);
+ res = -EPERM;
+ }
+ goto out;
+ }
+
+ if (command[0] == '-') {
+ list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list)
+ if (strnicmp(bond->dev->name, ifname, IFNAMSIZ) == 0) {
+ rtnl_lock();
+ /* check the ref count on the bond's kobject.
+ * If it's > expected, then there's a file open,
+ * and we have to fail.
+ */
+ if (atomic_read(&bond->dev->class_dev.kobj.kref.refcount)
+ > expected_refcount){
+ rtnl_unlock();
+ printk(KERN_INFO DRV_NAME
+ ": Unable remove bond %s due to open references.\n",
+ ifname);
+ res = -EPERM;
+ goto out;
+ }
+ printk(KERN_INFO DRV_NAME
+ ": %s is being deleted...\n",
+ bond->dev->name);
+ unregister_netdevice(bond->dev);
+ bond_deinit(bond->dev);
+ bond_destroy_sysfs_entry(bond);
+ rtnl_unlock();
+ goto out;
+ }
+
+ printk(KERN_ERR DRV_NAME
+ ": unable to delete non-existent bond %s\n", ifname);
+ res = -ENODEV;
+ goto out;
+ }
+
+err_no_cmd:
+ printk(KERN_ERR DRV_NAME
+ ": no command found in bonding_masters. Use +ifname or -ifname.\n");
+ res = -EPERM;
+
+ /* Always return either count or an error. If you return 0, you'll
+ * get called forever, which is bad.
+ */
+out:
+ up_write(&(bonding_rwsem));
+ return res;
+}
+/* class attribute for bond_masters file. This ends up in /sys/class/net */
+static CLASS_ATTR(bonding_masters, S_IWUSR | S_IRUGO,
+ bonding_show_bonds, bonding_store_bonds);
+
+int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave)
+{
+ char linkname[IFNAMSIZ+7];
+ int ret = 0;
+
+ /* first, create a link from the slave back to the master */
+ ret = sysfs_create_link(&(slave->class_dev.kobj), &(master->class_dev.kobj),
+ "master");
+ if (ret)
+ return ret;
+ /* next, create a link from the master to the slave */
+ sprintf(linkname,"slave_%s",slave->name);
+ ret = sysfs_create_link(&(master->class_dev.kobj), &(slave->class_dev.kobj),
+ linkname);
+ return ret;
+
+}
+
+void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave)
+{
+ char linkname[IFNAMSIZ+7];
+
+ sysfs_remove_link(&(slave->class_dev.kobj), "master");
+ sprintf(linkname,"slave_%s",slave->name);
+ sysfs_remove_link(&(master->class_dev.kobj), linkname);
+}
+
+
+/*
+ * Show the slaves in the current bond.
+ */
+static ssize_t bonding_show_slaves(struct class_device *cd, char *buf)
+{
+ struct slave *slave;
+ int i, res = 0;
+ struct bonding *bond = to_bond(cd);
+
+ read_lock_bh(&bond->lock);
+ bond_for_each_slave(bond, slave, i) {
+ if (res > (PAGE_SIZE - IFNAMSIZ)) {
+ /* not enough space for another interface name */
+ if ((PAGE_SIZE - res) > 10)
+ res = PAGE_SIZE - 10;
+ res += sprintf(buf + res, "++more++");
+ break;
+ }
+ res += sprintf(buf + res, "%s ", slave->dev->name);
+ }
+ read_unlock_bh(&bond->lock);
+ res += sprintf(buf + res, "\n");
+ res++;
+ return res;
+}
+
+/*
+ * Set the slaves in the current bond. The bond interface must be
+ * up for this to succeed.
+ * This function is largely the same flow as bonding_update_bonds().
+ */
+static ssize_t bonding_store_slaves(struct class_device *cd, const char *buffer, size_t count)
+{
+ char command[IFNAMSIZ + 1] = { 0, };
+ char *ifname;
+ int i, res, found, ret = count;
+ struct slave *slave;
+ struct net_device *dev = 0;
+ struct bonding *bond = to_bond(cd);
+
+ /* Quick sanity check -- is the bond interface up? */
+ if (!(bond->dev->flags & IFF_UP)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Unable to update slaves because interface is down.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ /* Note: We can't hold bond->lock here, as bond_create grabs it. */
+
+ sscanf(buffer, "%16s", command); /* IFNAMSIZ*/
+ ifname = command + 1;
+ if ((strlen(command) <= 1) ||
+ !dev_valid_name(ifname))
+ goto err_no_cmd;
+
+ if (command[0] == '+') {
+
+ /* Got a slave name in ifname. Is it already in the list? */
+ found = 0;
+ read_lock_bh(&bond->lock);
+ bond_for_each_slave(bond, slave, i)
+ if (strnicmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Interface %s is already enslaved!\n",
+ bond->dev->name, ifname);
+ ret = -EPERM;
+ read_unlock_bh(&bond->lock);
+ goto out;
+ }
+
+ read_unlock_bh(&bond->lock);
+ printk(KERN_INFO DRV_NAME ": %s: Adding slave %s.\n",
+ bond->dev->name, ifname);
+ dev = dev_get_by_name(ifname);
+ if (!dev) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Interface %s does not exist!\n",
+ bond->dev->name, ifname);
+ ret = -EPERM;
+ goto out;
+ }
+ else
+ dev_put(dev);
+
+ if (dev->flags & IFF_UP) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Error: Unable to enslave %s "
+ "because it is already up.\n",
+ bond->dev->name, dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+ /* If this is the first slave, then we need to set
+ the master's hardware address to be the same as the
+ slave's. */
+ if (!(*((u32 *) & (bond->dev->dev_addr[0])))) {
+ memcpy(bond->dev->dev_addr, dev->dev_addr,
+ dev->addr_len);
+ }
+
+ /* Set the slave's MTU to match the bond */
+ if (dev->mtu != bond->dev->mtu) {
+ if (dev->change_mtu) {
+ res = dev->change_mtu(dev,
+ bond->dev->mtu);
+ if (res) {
+ ret = res;
+ goto out;
+ }
+ } else {
+ dev->mtu = bond->dev->mtu;
+ }
+ }
+ rtnl_lock();
+ res = bond_enslave(bond->dev, dev);
+ rtnl_unlock();
+ if (res) {
+ ret = res;
+ }
+ goto out;
+ }
+
+ if (command[0] == '-') {
+ dev = NULL;
+ bond_for_each_slave(bond, slave, i)
+ if (strnicmp(slave->dev->name, ifname, IFNAMSIZ) == 0) {
+ dev = slave->dev;
+ break;
+ }
+ if (dev) {
+ printk(KERN_INFO DRV_NAME ": %s: Removing slave %s\n",
+ bond->dev->name, dev->name);
+ rtnl_lock();
+ res = bond_release(bond->dev, dev);
+ rtnl_unlock();
+ if (res) {
+ ret = res;
+ goto out;
+ }
+ /* set the slave MTU to the default */
+ if (dev->change_mtu) {
+ dev->change_mtu(dev, 1500);
+ } else {
+ dev->mtu = 1500;
+ }
+ }
+ else {
+ printk(KERN_ERR DRV_NAME ": unable to remove non-existent slave %s for bond %s.\n",
+ ifname, bond->dev->name);
+ ret = -ENODEV;
+ }
+ goto out;
+ }
+
+err_no_cmd:
+ printk(KERN_ERR DRV_NAME ": no command found in slaves file for bond %s. Use +ifname or -ifname.\n", bond->dev->name);
+ ret = -EPERM;
+
+out:
+ return ret;
+}
+
+static CLASS_DEVICE_ATTR(slaves, S_IRUGO | S_IWUSR, bonding_show_slaves, bonding_store_slaves);
+
+/*
+ * Show and set the bonding mode. The bond interface must be down to
+ * change the mode.
+ */
+static ssize_t bonding_show_mode(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%s %d\n",
+ bond_mode_tbl[bond->params.mode].modename,
+ bond->params.mode) + 1;
+}
+
+static ssize_t bonding_store_mode(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->dev->flags & IFF_UP) {
+ printk(KERN_ERR DRV_NAME
+ ": unable to update mode of %s because interface is up.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ new_value = bond_parse_parm((char *)buf, bond_mode_tbl);
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Ignoring invalid mode value %.*s.\n",
+ bond->dev->name,
+ (int)strlen(buf) - 1, buf);
+ ret = -EINVAL;
+ goto out;
+ } else {
+ bond->params.mode = new_value;
+ bond_set_mode_ops(bond, bond->params.mode);
+ printk(KERN_INFO DRV_NAME ": %s: setting mode to %s (%d).\n",
+ bond->dev->name, bond_mode_tbl[new_value].modename, new_value);
+ }
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, bonding_show_mode, bonding_store_mode);
+
+/*
+ * Show and set the bonding transmit hash method. The bond interface must be down to
+ * change the xmit hash policy.
+ */
+static ssize_t bonding_show_xmit_hash(struct class_device *cd, char *buf)
+{
+ int count;
+ struct bonding *bond = to_bond(cd);
+
+ if ((bond->params.mode != BOND_MODE_XOR) &&
+ (bond->params.mode != BOND_MODE_8023AD)) {
+ // Not Applicable
+ count = sprintf(buf, "NA\n") + 1;
+ } else {
+ count = sprintf(buf, "%s %d\n",
+ xmit_hashtype_tbl[bond->params.xmit_policy].modename,
+ bond->params.xmit_policy) + 1;
+ }
+
+ return count;
+}
+
+static ssize_t bonding_store_xmit_hash(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->dev->flags & IFF_UP) {
+ printk(KERN_ERR DRV_NAME
+ "%s: Interface is up. Unable to update xmit policy.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ if ((bond->params.mode != BOND_MODE_XOR) &&
+ (bond->params.mode != BOND_MODE_8023AD)) {
+ printk(KERN_ERR DRV_NAME
+ "%s: Transmit hash policy is irrelevant in this mode.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ new_value = bond_parse_parm((char *)buf, xmit_hashtype_tbl);
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Ignoring invalid xmit hash policy value %.*s.\n",
+ bond->dev->name,
+ (int)strlen(buf) - 1, buf);
+ ret = -EINVAL;
+ goto out;
+ } else {
+ bond->params.xmit_policy = new_value;
+ bond_set_mode_ops(bond, bond->params.mode);
+ printk(KERN_INFO DRV_NAME ": %s: setting xmit hash policy to %s (%d).\n",
+ bond->dev->name, xmit_hashtype_tbl[new_value].modename, new_value);
+ }
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR, bonding_show_xmit_hash, bonding_store_xmit_hash);
+
+/*
+ * Show and set the arp timer interval. There are two tricky bits
+ * here. First, if ARP monitoring is activated, then we must disable
+ * MII monitoring. Second, if the ARP timer isn't running, we must
+ * start it.
+ */
+static ssize_t bonding_show_arp_interval(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%d\n", bond->params.arp_interval) + 1;
+}
+
+static ssize_t bonding_store_arp_interval(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+ if (sscanf(buf, "%d", &new_value) != 1) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: no arp_interval value specified.\n",
+ bond->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Invalid arp_interval value %d not in range 1-%d; rejected.\n",
+ bond->dev->name, new_value, INT_MAX);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting ARP monitoring interval to %d.\n",
+ bond->dev->name, new_value);
+ bond->params.arp_interval = new_value;
+ if (bond->params.miimon) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: ARP monitoring cannot be used with MII monitoring. "
+ "%s Disabling MII monitoring.\n",
+ bond->dev->name, bond->dev->name);
+ bond->params.miimon = 0;
+ /* Kill MII timer, else it brings bond's link down */
+ if (bond->arp_timer.function) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Kill MII timer, else it brings bond's link down...\n",
+ bond->dev->name);
+ del_timer_sync(&bond->mii_timer);
+ }
+ }
+ if (!bond->params.arp_targets[0]) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: ARP monitoring has been set up, "
+ "but no ARP targets have been specified.\n",
+ bond->dev->name);
+ }
+ if (bond->dev->flags & IFF_UP) {
+ /* If the interface is up, we may need to fire off
+ * the ARP timer. If the interface is down, the
+ * timer will get fired off when the open function
+ * is called.
+ */
+ if (bond->arp_timer.function) {
+ /* The timer's already set up, so fire it off */
+ mod_timer(&bond->arp_timer, jiffies + 1);
+ } else {
+ /* Set up the timer. */
+ init_timer(&bond->arp_timer);
+ bond->arp_timer.expires = jiffies + 1;
+ bond->arp_timer.data =
+ (unsigned long) bond->dev;
+ if (bond->params.mode == BOND_MODE_ACTIVEBACKUP) {
+ bond->arp_timer.function =
+ (void *)
+ &bond_activebackup_arp_mon;
+ } else {
+ bond->arp_timer.function =
+ (void *)
+ &bond_loadbalance_arp_mon;
+ }
+ add_timer(&bond->arp_timer);
+ }
+ }
+
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(arp_interval, S_IRUGO | S_IWUSR , bonding_show_arp_interval, bonding_store_arp_interval);
+
+/*
+ * Show and set the arp targets.
+ */
+static ssize_t bonding_show_arp_targets(struct class_device *cd, char *buf)
+{
+ int i, res = 0;
+ struct bonding *bond = to_bond(cd);
+
+ for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
+ if (bond->params.arp_targets[i])
+ res += sprintf(buf + res, "%u.%u.%u.%u ",
+ NIPQUAD(bond->params.arp_targets[i]));
+ }
+ if (res)
+ res--; /* eat the leftover space */
+ res += sprintf(buf + res, "\n");
+ res++;
+ return res;
+}
+
+static ssize_t bonding_store_arp_targets(struct class_device *cd, const char *buf, size_t count)
+{
+ u32 newtarget;
+ int i = 0, done = 0, ret = count;
+ struct bonding *bond = to_bond(cd);
+ u32 *targets;
+
+ targets = bond->params.arp_targets;
+ newtarget = in_aton(buf + 1);
+ /* look for adds */
+ if (buf[0] == '+') {
+ if ((newtarget == 0) || (newtarget == INADDR_BROADCAST)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: invalid ARP target %u.%u.%u.%u specified for addition\n",
+ bond->dev->name, NIPQUAD(newtarget));
+ ret = -EINVAL;
+ goto out;
+ }
+ /* look for an empty slot to put the target in, and check for dupes */
+ for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
+ if (targets[i] == newtarget) { /* duplicate */
+ printk(KERN_ERR DRV_NAME
+ ": %s: ARP target %u.%u.%u.%u is already present\n",
+ bond->dev->name, NIPQUAD(newtarget));
+ if (done)
+ targets[i] = 0;
+ ret = -EINVAL;
+ goto out;
+ }
+ if (targets[i] == 0 && !done) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: adding ARP target %d.%d.%d.%d.\n",
+ bond->dev->name, NIPQUAD(newtarget));
+ done = 1;
+ targets[i] = newtarget;
+ }
+ }
+ if (!done) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: ARP target table is full!\n",
+ bond->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ }
+ else if (buf[0] == '-') {
+ if ((newtarget == 0) || (newtarget == INADDR_BROADCAST)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: invalid ARP target %d.%d.%d.%d specified for removal\n",
+ bond->dev->name, NIPQUAD(newtarget));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
+ if (targets[i] == newtarget) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: removing ARP target %d.%d.%d.%d.\n",
+ bond->dev->name, NIPQUAD(newtarget));
+ targets[i] = 0;
+ done = 1;
+ }
+ }
+ if (!done) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: unable to remove nonexistent ARP target %d.%d.%d.%d.\n",
+ bond->dev->name, NIPQUAD(newtarget));
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ else {
+ printk(KERN_ERR DRV_NAME ": no command found in arp_ip_targets file for bond %s. Use +<addr> or -<addr>.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets);
+
+/*
+ * Show and set the up and down delays. These must be multiples of the
+ * MII monitoring value, and are stored internally as the multiplier.
+ * Thus, we must translate to MS for the real world.
+ */
+static ssize_t bonding_show_downdelay(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%d\n", bond->params.downdelay * bond->params.miimon) + 1;
+}
+
+static ssize_t bonding_store_downdelay(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+ if (!(bond->params.miimon)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Unable to set down delay as MII monitoring is disabled\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (sscanf(buf, "%d", &new_value) != 1) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: no down delay value specified.\n",
+ bond->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Invalid down delay value %d not in range %d-%d; rejected.\n",
+ bond->dev->name, new_value, 1, INT_MAX);
+ ret = -EINVAL;
+ goto out;
+ } else {
+ if ((new_value % bond->params.miimon) != 0) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Warning: down delay (%d) is not a multiple "
+ "of miimon (%d), delay rounded to %d ms\n",
+ bond->dev->name, new_value, bond->params.miimon,
+ (new_value / bond->params.miimon) *
+ bond->params.miimon);
+ }
+ bond->params.downdelay = new_value / bond->params.miimon;
+ printk(KERN_INFO DRV_NAME ": %s: Setting down delay to %d.\n",
+ bond->dev->name, bond->params.downdelay * bond->params.miimon);
+
+ }
+
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(downdelay, S_IRUGO | S_IWUSR , bonding_show_downdelay, bonding_store_downdelay);
+
+static ssize_t bonding_show_updelay(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%d\n", bond->params.updelay * bond->params.miimon) + 1;
+
+}
+
+static ssize_t bonding_store_updelay(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+ if (!(bond->params.miimon)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Unable to set up delay as MII monitoring is disabled\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (sscanf(buf, "%d", &new_value) != 1) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: no up delay value specified.\n",
+ bond->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Invalid down delay value %d not in range %d-%d; rejected.\n",
+ bond->dev->name, new_value, 1, INT_MAX);
+ ret = -EINVAL;
+ goto out;
+ } else {
+ if ((new_value % bond->params.miimon) != 0) {
+ printk(KERN_WARNING DRV_NAME
+ ": %s: Warning: up delay (%d) is not a multiple "
+ "of miimon (%d), updelay rounded to %d ms\n",
+ bond->dev->name, new_value, bond->params.miimon,
+ (new_value / bond->params.miimon) *
+ bond->params.miimon);
+ }
+ bond->params.updelay = new_value / bond->params.miimon;
+ printk(KERN_INFO DRV_NAME ": %s: Setting up delay to %d.\n",
+ bond->dev->name, bond->params.updelay * bond->params.miimon);
+
+ }
+
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(updelay, S_IRUGO | S_IWUSR , bonding_show_updelay, bonding_store_updelay);
+
+/*
+ * Show and set the LACP interval. Interface must be down, and the mode
+ * must be set to 802.3ad mode.
+ */
+static ssize_t bonding_show_lacp(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%s %d\n",
+ bond_lacp_tbl[bond->params.lacp_fast].modename,
+ bond->params.lacp_fast) + 1;
+}
+
+static ssize_t bonding_store_lacp(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->dev->flags & IFF_UP) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Unable to update LACP rate because interface is up.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (bond->params.mode != BOND_MODE_8023AD) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Unable to update LACP rate because bond is not in 802.3ad mode.\n",
+ bond->dev->name);
+ ret = -EPERM;
+ goto out;
+ }
+
+ new_value = bond_parse_parm((char *)buf, bond_lacp_tbl);
+
+ if ((new_value == 1) || (new_value == 0)) {
+ bond->params.lacp_fast = new_value;
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting LACP rate to %s (%d).\n",
+ bond->dev->name, bond_lacp_tbl[new_value].modename, new_value);
+ } else {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Ignoring invalid LACP rate value %.*s.\n",
+ bond->dev->name, (int)strlen(buf) - 1, buf);
+ ret = -EINVAL;
+ }
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(lacp_rate, S_IRUGO | S_IWUSR, bonding_show_lacp, bonding_store_lacp);
+
+/*
+ * Show and set the MII monitor interval. There are two tricky bits
+ * here. First, if MII monitoring is activated, then we must disable
+ * ARP monitoring. Second, if the timer isn't running, we must
+ * start it.
+ */
+static ssize_t bonding_show_miimon(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%d\n", bond->params.miimon) + 1;
+}
+
+static ssize_t bonding_store_miimon(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+ if (sscanf(buf, "%d", &new_value) != 1) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: no miimon value specified.\n",
+ bond->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Invalid miimon value %d not in range %d-%d; rejected.\n",
+ bond->dev->name, new_value, 1, INT_MAX);
+ ret = -EINVAL;
+ goto out;
+ } else {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting MII monitoring interval to %d.\n",
+ bond->dev->name, new_value);
+ bond->params.miimon = new_value;
+ if(bond->params.updelay)
+ printk(KERN_INFO DRV_NAME
+ ": %s: Note: Updating updelay (to %d) "
+ "since it is a multiple of the miimon value.\n",
+ bond->dev->name,
+ bond->params.updelay * bond->params.miimon);
+ if(bond->params.downdelay)
+ printk(KERN_INFO DRV_NAME
+ ": %s: Note: Updating downdelay (to %d) "
+ "since it is a multiple of the miimon value.\n",
+ bond->dev->name,
+ bond->params.downdelay * bond->params.miimon);
+ if (bond->params.arp_interval) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: MII monitoring cannot be used with "
+ "ARP monitoring. Disabling ARP monitoring...\n",
+ bond->dev->name);
+ bond->params.arp_interval = 0;
+ /* Kill ARP timer, else it brings bond's link down */
+ if (bond->mii_timer.function) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Kill ARP timer, else it brings bond's link down...\n",
+ bond->dev->name);
+ del_timer_sync(&bond->arp_timer);
+ }
+ }
+
+ if (bond->dev->flags & IFF_UP) {
+ /* If the interface is up, we may need to fire off
+ * the MII timer. If the interface is down, the
+ * timer will get fired off when the open function
+ * is called.
+ */
+ if (bond->mii_timer.function) {
+ /* The timer's already set up, so fire it off */
+ mod_timer(&bond->mii_timer, jiffies + 1);
+ } else {
+ /* Set up the timer. */
+ init_timer(&bond->mii_timer);
+ bond->mii_timer.expires = jiffies + 1;
+ bond->mii_timer.data =
+ (unsigned long) bond->dev;
+ bond->mii_timer.function =
+ (void *) &bond_mii_monitor;
+ add_timer(&bond->mii_timer);
+ }
+ }
+ }
+out:
+ return ret;
+}
+static CLASS_DEVICE_ATTR(miimon, S_IRUGO | S_IWUSR, bonding_show_miimon, bonding_store_miimon);
+
+/*
+ * Show and set the primary slave. The store function is much
+ * simpler than bonding_store_slaves function because it only needs to
+ * handle one interface name.
+ * The bond must be a mode that supports a primary for this be
+ * set.
+ */
+static ssize_t bonding_show_primary(struct class_device *cd, char *buf)
+{
+ int count = 0;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->primary_slave)
+ count = sprintf(buf, "%s\n", bond->primary_slave->dev->name) + 1;
+ else
+ count = sprintf(buf, "\n") + 1;
+
+ return count;
+}
+
+static ssize_t bonding_store_primary(struct class_device *cd, const char *buf, size_t count)
+{
+ int i;
+ struct slave *slave;
+ struct bonding *bond = to_bond(cd);
+
+ write_lock_bh(&bond->lock);
+ if (!USES_PRIMARY(bond->params.mode)) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Unable to set primary slave; %s is in mode %d\n",
+ bond->dev->name, bond->dev->name, bond->params.mode);
+ } else {
+ bond_for_each_slave(bond, slave, i) {
+ if (strnicmp
+ (slave->dev->name, buf,
+ strlen(slave->dev->name)) == 0) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting %s as primary slave.\n",
+ bond->dev->name, slave->dev->name);
+ bond->primary_slave = slave;
+ bond_select_active_slave(bond);
+ goto out;
+ }
+ }
+
+ /* if we got here, then we didn't match the name of any slave */
+
+ if (strlen(buf) == 0 || buf[0] == '\n') {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting primary slave to None.\n",
+ bond->dev->name);
+ bond->primary_slave = 0;
+ bond_select_active_slave(bond);
+ } else {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Unable to set %.*s as primary slave as it is not a slave.\n",
+ bond->dev->name, (int)strlen(buf) - 1, buf);
+ }
+ }
+out:
+ write_unlock_bh(&bond->lock);
+ return count;
+}
+static CLASS_DEVICE_ATTR(primary, S_IRUGO | S_IWUSR, bonding_show_primary, bonding_store_primary);
+
+/*
+ * Show and set the use_carrier flag.
+ */
+static ssize_t bonding_show_carrier(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%d\n", bond->params.use_carrier) + 1;
+}
+
+static ssize_t bonding_store_carrier(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value, ret = count;
+ struct bonding *bond = to_bond(cd);
+
+
+ if (sscanf(buf, "%d", &new_value) != 1) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: no use_carrier value specified.\n",
+ bond->dev->name);
+ ret = -EINVAL;
+ goto out;
+ }
+ if ((new_value == 0) || (new_value == 1)) {
+ bond->params.use_carrier = new_value;
+ printk(KERN_INFO DRV_NAME ": %s: Setting use_carrier to %d.\n",
+ bond->dev->name, new_value);
+ } else {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Ignoring invalid use_carrier value %d.\n",
+ bond->dev->name, new_value);
+ }
+out:
+ return count;
+}
+static CLASS_DEVICE_ATTR(use_carrier, S_IRUGO | S_IWUSR, bonding_show_carrier, bonding_store_carrier);
+
+
+/*
+ * Show and set currently active_slave.
+ */
+static ssize_t bonding_show_active_slave(struct class_device *cd, char *buf)
+{
+ struct slave *curr;
+ struct bonding *bond = to_bond(cd);
+ int count;
+
+
+ read_lock(&bond->curr_slave_lock);
+ curr = bond->curr_active_slave;
+ read_unlock(&bond->curr_slave_lock);
+
+ if (USES_PRIMARY(bond->params.mode) && curr)
+ count = sprintf(buf, "%s\n", curr->dev->name) + 1;
+ else
+ count = sprintf(buf, "\n") + 1;
+ return count;
+}
+
+static ssize_t bonding_store_active_slave(struct class_device *cd, const char *buf, size_t count)
+{
+ int i;
+ struct slave *slave;
+ struct slave *old_active = NULL;
+ struct slave *new_active = NULL;
+ struct bonding *bond = to_bond(cd);
+
+ write_lock_bh(&bond->lock);
+ if (!USES_PRIMARY(bond->params.mode)) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Unable to change active slave; %s is in mode %d\n",
+ bond->dev->name, bond->dev->name, bond->params.mode);
+ } else {
+ bond_for_each_slave(bond, slave, i) {
+ if (strnicmp
+ (slave->dev->name, buf,
+ strlen(slave->dev->name)) == 0) {
+ old_active = bond->curr_active_slave;
+ new_active = slave;
+ if (new_active && (new_active == old_active)) {
+ /* do nothing */
+ printk(KERN_INFO DRV_NAME
+ ": %s: %s is already the current active slave.\n",
+ bond->dev->name, slave->dev->name);
+ goto out;
+ }
+ else {
+ if ((new_active) &&
+ (old_active) &&
+ (new_active->link == BOND_LINK_UP) &&
+ IS_UP(new_active->dev)) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting %s as active slave.\n",
+ bond->dev->name, slave->dev->name);
+ bond_change_active_slave(bond, new_active);
+ }
+ else {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Could not set %s as active slave; "
+ "either %s is down or the link is down.\n",
+ bond->dev->name, slave->dev->name,
+ slave->dev->name);
+ }
+ goto out;
+ }
+ }
+ }
+
+ /* if we got here, then we didn't match the name of any slave */
+
+ if (strlen(buf) == 0 || buf[0] == '\n') {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Setting active slave to None.\n",
+ bond->dev->name);
+ bond->primary_slave = 0;
+ bond_select_active_slave(bond);
+ } else {
+ printk(KERN_INFO DRV_NAME
+ ": %s: Unable to set %.*s as active slave as it is not a slave.\n",
+ bond->dev->name, (int)strlen(buf) - 1, buf);
+ }
+ }
+out:
+ write_unlock_bh(&bond->lock);
+ return count;
+
+}
+static CLASS_DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR, bonding_show_active_slave, bonding_store_active_slave);
+
+
+/*
+ * Show link status of the bond interface.
+ */
+static ssize_t bonding_show_mii_status(struct class_device *cd, char *buf)
+{
+ struct slave *curr;
+ struct bonding *bond = to_bond(cd);
+
+ read_lock(&bond->curr_slave_lock);
+ curr = bond->curr_active_slave;
+ read_unlock(&bond->curr_slave_lock);
+
+ return sprintf(buf, "%s\n", (curr) ? "up" : "down") + 1;
+}
+static CLASS_DEVICE_ATTR(mii_status, S_IRUGO, bonding_show_mii_status, NULL);
+
+
+/*
+ * Show current 802.3ad aggregator ID.
+ */
+static ssize_t bonding_show_ad_aggregator(struct class_device *cd, char *buf)
+{
+ int count = 0;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->params.mode == BOND_MODE_8023AD) {
+ struct ad_info ad_info;
+ count = sprintf(buf, "%d\n", (bond_3ad_get_active_agg_info(bond, &ad_info)) ? 0 : ad_info.aggregator_id) + 1;
+ }
+ else
+ count = sprintf(buf, "\n") + 1;
+
+ return count;
+}
+static CLASS_DEVICE_ATTR(ad_aggregator, S_IRUGO, bonding_show_ad_aggregator, NULL);
+
+
+/*
+ * Show number of active 802.3ad ports.
+ */
+static ssize_t bonding_show_ad_num_ports(struct class_device *cd, char *buf)
+{
+ int count = 0;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->params.mode == BOND_MODE_8023AD) {
+ struct ad_info ad_info;
+ count = sprintf(buf, "%d\n", (bond_3ad_get_active_agg_info(bond, &ad_info)) ? 0: ad_info.ports) + 1;
+ }
+ else
+ count = sprintf(buf, "\n") + 1;
+
+ return count;
+}
+static CLASS_DEVICE_ATTR(ad_num_ports, S_IRUGO, bonding_show_ad_num_ports, NULL);
+
+
+/*
+ * Show current 802.3ad actor key.
+ */
+static ssize_t bonding_show_ad_actor_key(struct class_device *cd, char *buf)
+{
+ int count = 0;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->params.mode == BOND_MODE_8023AD) {
+ struct ad_info ad_info;
+ count = sprintf(buf, "%d\n", (bond_3ad_get_active_agg_info(bond, &ad_info)) ? 0 : ad_info.actor_key) + 1;
+ }
+ else
+ count = sprintf(buf, "\n") + 1;
+
+ return count;
+}
+static CLASS_DEVICE_ATTR(ad_actor_key, S_IRUGO, bonding_show_ad_actor_key, NULL);
+
+
+/*
+ * Show current 802.3ad partner key.
+ */
+static ssize_t bonding_show_ad_partner_key(struct class_device *cd, char *buf)
+{
+ int count = 0;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->params.mode == BOND_MODE_8023AD) {
+ struct ad_info ad_info;
+ count = sprintf(buf, "%d\n", (bond_3ad_get_active_agg_info(bond, &ad_info)) ? 0 : ad_info.partner_key) + 1;
+ }
+ else
+ count = sprintf(buf, "\n") + 1;
+
+ return count;
+}
+static CLASS_DEVICE_ATTR(ad_partner_key, S_IRUGO, bonding_show_ad_partner_key, NULL);
+
+
+/*
+ * Show current 802.3ad partner mac.
+ */
+static ssize_t bonding_show_ad_partner_mac(struct class_device *cd, char *buf)
+{
+ int count = 0;
+ struct bonding *bond = to_bond(cd);
+
+ if (bond->params.mode == BOND_MODE_8023AD) {
+ struct ad_info ad_info;
+ if (!bond_3ad_get_active_agg_info(bond, &ad_info)) {
+ count = sprintf(buf,"%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ad_info.partner_system[0],
+ ad_info.partner_system[1],
+ ad_info.partner_system[2],
+ ad_info.partner_system[3],
+ ad_info.partner_system[4],
+ ad_info.partner_system[5]) + 1;
+ }
+ }
+ else
+ count = sprintf(buf, "\n") + 1;
+
+ return count;
+}
+static CLASS_DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, NULL);
+
+
+
+static struct attribute *per_bond_attrs[] = {
+ &class_device_attr_slaves.attr,
+ &class_device_attr_mode.attr,
+ &class_device_attr_arp_interval.attr,
+ &class_device_attr_arp_ip_target.attr,
+ &class_device_attr_downdelay.attr,
+ &class_device_attr_updelay.attr,
+ &class_device_attr_lacp_rate.attr,
+ &class_device_attr_xmit_hash_policy.attr,
+ &class_device_attr_miimon.attr,
+ &class_device_attr_primary.attr,
+ &class_device_attr_use_carrier.attr,
+ &class_device_attr_active_slave.attr,
+ &class_device_attr_mii_status.attr,
+ &class_device_attr_ad_aggregator.attr,
+ &class_device_attr_ad_num_ports.attr,
+ &class_device_attr_ad_actor_key.attr,
+ &class_device_attr_ad_partner_key.attr,
+ &class_device_attr_ad_partner_mac.attr,
+ NULL,
+};
+
+static struct attribute_group bonding_group = {
+ .name = "bonding",
+ .attrs = per_bond_attrs,
+};
+
+/*
+ * Initialize sysfs. This sets up the bonding_masters file in
+ * /sys/class/net.
+ */
+int bond_create_sysfs(void)
+{
+ int ret = 0;
+ struct bonding *firstbond;
+
+ init_rwsem(&bonding_rwsem);
+
+ /* get the netdev class pointer */
+ firstbond = container_of(bond_dev_list.next, struct bonding, bond_list);
+ if (!firstbond)
+ return -ENODEV;
+
+ netdev_class = firstbond->dev->class_dev.class;
+ if (!netdev_class)
+ return -ENODEV;
+
+ ret = class_create_file(netdev_class, &class_attr_bonding_masters);
+
+ return ret;
+
+}
+
+/*
+ * Remove /sys/class/net/bonding_masters.
+ */
+void bond_destroy_sysfs(void)
+{
+ if (netdev_class)
+ class_remove_file(netdev_class, &class_attr_bonding_masters);
+}
+
+/*
+ * Initialize sysfs for each bond. This sets up and registers
+ * the 'bondctl' directory for each individual bond under /sys/class/net.
+ */
+int bond_create_sysfs_entry(struct bonding *bond)
+{
+ struct net_device *dev = bond->dev;
+ int err;
+
+ err = sysfs_create_group(&(dev->class_dev.kobj), &bonding_group);
+ if (err) {
+ printk(KERN_EMERG "eek! didn't create group!\n");
+ }
+
+ if (expected_refcount < 1)
+ expected_refcount = atomic_read(&bond->dev->class_dev.kobj.kref.refcount);
+
+ return err;
+}
+/*
+ * Remove sysfs entries for each bond.
+ */
+void bond_destroy_sysfs_entry(struct bonding *bond)
+{
+ struct net_device *dev = bond->dev;
+
+ sysfs_remove_group(&(dev->class_dev.kobj), &bonding_group);
+}
+
* 2005/05/05 - Jason Gabler <jygabler at lbl dot gov>
* - added "xmit_policy" kernel parameter for alternate hashing policy
* support for mode 2
+ *
+ * 2005/09/27 - Mitch Williams <mitch.a.williams at intel dot com>
+ * Radheka Godse <radheka.godse at intel dot com>
+ * - Added bonding sysfs interface
*/
#ifndef _LINUX_BONDING_H
#include <linux/timer.h>
#include <linux/proc_fs.h>
#include <linux/if_bonding.h>
+#include <linux/kobject.h>
#include "bond_3ad.h"
#include "bond_alb.h"
-#define DRV_VERSION "2.6.5"
-#define DRV_RELDATE "November 4, 2005"
+#define DRV_VERSION "3.0.0"
+#define DRV_RELDATE "November 8, 2005"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
u32 arp_targets[BOND_MAX_ARP_TARGETS];
};
+struct bond_parm_tbl {
+ char *modename;
+ int mode;
+};
+
struct vlan_entry {
struct list_head vlan_list;
u32 vlan_ip;
};
struct slave {
- struct net_device *dev; /* first - usefull for panic debug */
+ struct net_device *dev; /* first - useful for panic debug */
struct slave *next;
struct slave *prev;
s16 delay;
* beforehand.
*/
struct bonding {
- struct net_device *dev; /* first - usefull for panic debug */
+ struct net_device *dev; /* first - useful for panic debug */
struct slave *first_slave;
struct slave *curr_active_slave;
struct slave *current_arp_slave;
struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr);
int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
+int bond_create(char *name, struct bond_params *params, struct bonding **newbond);
+void bond_deinit(struct net_device *bond_dev);
+int bond_create_sysfs(void);
+void bond_destroy_sysfs(void);
+void bond_destroy_sysfs_entry(struct bonding *bond);
+int bond_create_sysfs_entry(struct bonding *bond);
+int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave);
+void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave);
+int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev);
+int bond_release(struct net_device *bond_dev, struct net_device *slave_dev);
+int bond_sethwaddr(struct net_device *bond_dev, struct net_device *slave_dev);
+void bond_mii_monitor(struct net_device *bond_dev);
+void bond_loadbalance_arp_mon(struct net_device *bond_dev);
+void bond_activebackup_arp_mon(struct net_device *bond_dev);
+void bond_set_mode_ops(struct bonding *bond, int mode);
+int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl);
+const char *bond_mode_name(int mode);
+void bond_select_active_slave(struct bonding *bond);
+void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
#endif /* _LINUX_BONDING_H */
* drivers/net/gianfar.c
*
* Gianfar Ethernet Driver
- * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * This driver is designed for the non-CPM ethernet controllers
+ * on the 85xx and 83xx family of integrated processors
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
* B-V +1.62
*
* Theory of operation
- * This driver is designed for the non-CPM ethernet controllers
- * on the 85xx and 83xx family of integrated processors
*
* The driver is initialized through platform_device. Structures which
* define the configuration needed by the board are defined in a
#endif
const char gfar_driver_name[] = "Gianfar Ethernet";
-const char gfar_driver_version[] = "1.2";
+const char gfar_driver_version[] = "1.3";
static int gfar_enet_open(struct net_device *dev);
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void gfar_vlan_rx_register(struct net_device *netdev,
struct vlan_group *grp);
static void gfar_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
+void gfar_halt(struct net_device *dev);
+void gfar_start(struct net_device *dev);
+static void gfar_clear_exact_match(struct net_device *dev);
+static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr);
extern struct ethtool_ops gfar_ethtool_ops;
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
MODULE_LICENSE("GPL");
-int gfar_uses_fcb(struct gfar_private *priv)
+/* Returns 1 if incoming frames use an FCB */
+static inline int gfar_uses_fcb(struct gfar_private *priv)
{
- if (priv->vlan_enable || priv->rx_csum_enable)
- return 1;
- else
- return 0;
+ return (priv->vlan_enable || priv->rx_csum_enable);
}
/* Set up the ethernet device structure, private data,
else
priv->padding = 0;
- dev->hard_header_len += priv->padding;
-
if (dev->features & NETIF_F_IP_CSUM)
dev->hard_header_len += GMAC_FCB_LEN;
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
-#ifdef CONFIG_GFAR_BUFSTASH
- priv->rx_stash_size = STASH_LENGTH;
-#endif
priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
goto register_fail;
}
+ /* Create all the sysfs files */
+ gfar_init_sysfs(dev);
+
/* Print out the device info */
printk(KERN_INFO DEVICE_NAME, dev->name);
for (idx = 0; idx < 6; idx++)
printk("\n");
/* Even more device info helps when determining which kernel */
- /* provided which set of benchmarks. Since this is global for all */
- /* devices, we only print it once */
+ /* provided which set of benchmarks. */
#ifdef CONFIG_GFAR_NAPI
printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
#else
/* Initialize the max receive buffer length */
gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
-#ifdef CONFIG_GFAR_BUFSTASH
- /* If we are stashing buffers, we need to set the
- * extraction length to the size of the buffer */
- gfar_write(&priv->regs->attreli, priv->rx_stash_size << 16);
-#endif
-
/* Initialize the Minimum Frame Length Register */
gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS);
- /* Setup Attributes so that snooping is on for rx */
- gfar_write(&priv->regs->attr, ATTR_INIT_SETTINGS);
- gfar_write(&priv->regs->attreli, ATTRELI_INIT_SETTINGS);
-
/* Assign the TBI an address which won't conflict with the PHYs */
gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
}
for (i = 0; i < priv->rx_ring_size; i++) {
if (priv->rx_skbuff[i]) {
dma_unmap_single(NULL, rxbdp->bufPtr,
- priv->rx_buffer_size
- + RXBUF_ALIGNMENT,
+ priv->rx_buffer_size,
DMA_FROM_DEVICE);
dev_kfree_skb_any(priv->rx_skbuff[i]);
struct gfar *regs = priv->regs;
int err = 0;
u32 rctrl = 0;
+ u32 attrs = 0;
gfar_write(®s->imask, IMASK_INIT_CLEAR);
if (priv->rx_csum_enable)
rctrl |= RCTRL_CHECKSUMMING;
- if (priv->extended_hash)
+ if (priv->extended_hash) {
rctrl |= RCTRL_EXTHASH;
+ gfar_clear_exact_match(dev);
+ rctrl |= RCTRL_EMEN;
+ }
+
if (priv->vlan_enable)
rctrl |= RCTRL_VLAN;
+ if (priv->padding) {
+ rctrl &= ~RCTRL_PAL_MASK;
+ rctrl |= RCTRL_PADDING(priv->padding);
+ }
+
/* Init rctrl based on our settings */
gfar_write(&priv->regs->rctrl, rctrl);
if (dev->features & NETIF_F_IP_CSUM)
gfar_write(&priv->regs->tctrl, TCTRL_INIT_CSUM);
+ /* Set the extraction length and index */
+ attrs = ATTRELI_EL(priv->rx_stash_size) |
+ ATTRELI_EI(priv->rx_stash_index);
+
+ gfar_write(&priv->regs->attreli, attrs);
+
+ /* Start with defaults, and add stashing or locking
+ * depending on the approprate variables */
+ attrs = ATTR_INIT_SETTINGS;
+
+ if (priv->bd_stash_en)
+ attrs |= ATTR_BDSTASH;
+
+ if (priv->rx_stash_size != 0)
+ attrs |= ATTR_BUFSTASH;
+
+ gfar_write(&priv->regs->attr, attrs);
+
+ gfar_write(&priv->regs->fifo_tx_thr, priv->fifo_threshold);
+ gfar_write(&priv->regs->fifo_tx_starve, priv->fifo_starve);
+ gfar_write(&priv->regs->fifo_tx_starve_shutoff, priv->fifo_starve_off);
+
+ /* Start the controller */
gfar_start(dev);
return 0;
return err;
}
-static struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp)
+static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp)
{
struct txfcb *fcb = (struct txfcb *)skb_push (skb, GMAC_FCB_LEN);
memset(fcb, 0, GMAC_FCB_LEN);
- /* Flag the bd so the controller looks for the FCB */
- bdp->status |= TXBD_TOE;
-
return fcb;
}
static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb)
{
- int len;
+ u8 flags = 0;
/* If we're here, it's a IP packet with a TCP or UDP
* payload. We set it to checksum, using a pseudo-header
* we provide
*/
- fcb->ip = 1;
- fcb->tup = 1;
- fcb->ctu = 1;
- fcb->nph = 1;
+ flags = TXFCB_DEFAULT;
- /* Notify the controller what the protocol is */
- if (skb->nh.iph->protocol == IPPROTO_UDP)
- fcb->udp = 1;
+ /* Tell the controller what the protocol is */
+ /* And provide the already calculated phcs */
+ if (skb->nh.iph->protocol == IPPROTO_UDP) {
+ flags |= TXFCB_UDP;
+ fcb->phcs = skb->h.uh->check;
+ } else
+ fcb->phcs = skb->h.th->check;
/* l3os is the distance between the start of the
* frame (skb->data) and the start of the IP hdr.
fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN);
fcb->l4os = (u16)(skb->h.raw - skb->nh.raw);
- len = skb->nh.iph->tot_len - fcb->l4os;
-
- /* Provide the pseudoheader csum */
- fcb->phcs = ~csum_tcpudp_magic(skb->nh.iph->saddr,
- skb->nh.iph->daddr, len,
- skb->nh.iph->protocol, 0);
+ fcb->flags = flags;
}
-void gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb)
+void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb)
{
- fcb->vln = 1;
+ fcb->flags |= TXFCB_VLN;
fcb->vlctl = vlan_tx_tag_get(skb);
}
struct gfar_private *priv = netdev_priv(dev);
struct txfcb *fcb = NULL;
struct txbd8 *txbdp;
+ u16 status;
/* Update transmit stats */
priv->stats.tx_bytes += skb->len;
txbdp = priv->cur_tx;
/* Clear all but the WRAP status flags */
- txbdp->status &= TXBD_WRAP;
+ status = txbdp->status & TXBD_WRAP;
/* Set up checksumming */
- if ((dev->features & NETIF_F_IP_CSUM)
- && (CHECKSUM_HW == skb->ip_summed)) {
+ if (likely((dev->features & NETIF_F_IP_CSUM)
+ && (CHECKSUM_HW == skb->ip_summed))) {
fcb = gfar_add_fcb(skb, txbdp);
+ status |= TXBD_TOE;
gfar_tx_checksum(skb, fcb);
}
if (priv->vlan_enable &&
unlikely(priv->vlgrp && vlan_tx_tag_present(skb))) {
- if (NULL == fcb)
+ if (unlikely(NULL == fcb)) {
fcb = gfar_add_fcb(skb, txbdp);
+ status |= TXBD_TOE;
+ }
gfar_tx_vlan(skb, fcb);
}
(priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size);
/* Flag the BD as interrupt-causing */
- txbdp->status |= TXBD_INTERRUPT;
+ status |= TXBD_INTERRUPT;
/* Flag the BD as ready to go, last in frame, and */
/* in need of CRC */
- txbdp->status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
+ status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
dev->trans_start = jiffies;
+ txbdp->status = status;
+
/* If this was the last BD in the ring, the next one */
/* is at the beginning of the ring */
if (txbdp->status & TXBD_WRAP)
/* Changes the mac address if the controller is not running. */
int gfar_set_mac_address(struct net_device *dev)
{
- struct gfar_private *priv = netdev_priv(dev);
- int i;
- char tmpbuf[MAC_ADDR_LEN];
- u32 tempval;
-
- /* Now copy it into the mac registers backwards, cuz */
- /* little endian is silly */
- for (i = 0; i < MAC_ADDR_LEN; i++)
- tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->dev_addr[i];
-
- gfar_write(&priv->regs->macstnaddr1, *((u32 *) (tmpbuf)));
-
- tempval = *((u32 *) (tmpbuf + 4));
-
- gfar_write(&priv->regs->macstnaddr2, tempval);
+ gfar_set_mac_for_addr(dev, 0, dev->dev_addr);
return 0;
}
INCREMENTAL_BUFFER_SIZE;
/* Only stop and start the controller if it isn't already
- * stopped */
+ * stopped, and we changed something */
if ((oldsize != tempsize) && (dev->flags & IFF_UP))
stop_gfar(dev);
struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
{
+ unsigned int alignamount;
struct gfar_private *priv = netdev_priv(dev);
struct sk_buff *skb = NULL;
unsigned int timeout = SKB_ALLOC_TIMEOUT;
if (NULL == skb)
return NULL;
+ alignamount = RXBUF_ALIGNMENT -
+ (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1));
+
/* We need the data buffer to be aligned properly. We will reserve
* as many bytes as needed to align the data properly
*/
- skb_reserve(skb,
- RXBUF_ALIGNMENT -
- (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1)));
+ skb_reserve(skb, alignamount);
skb->dev = dev;
bdp->bufPtr = dma_map_single(NULL, skb->data,
- priv->rx_buffer_size + RXBUF_ALIGNMENT,
- DMA_FROM_DEVICE);
+ priv->rx_buffer_size, DMA_FROM_DEVICE);
bdp->length = 0;
/* If valid headers were found, and valid sums
* were verified, then we tell the kernel that no
* checksumming is necessary. Otherwise, it is */
- if (fcb->cip && !fcb->eip && fcb->ctu && !fcb->etu)
+ if ((fcb->flags & RXFCB_CSUM_MASK) == (RXFCB_CIP | RXFCB_CTU))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
skb->protocol = eth_type_trans(skb, dev);
/* Send the packet up the stack */
- if (unlikely(priv->vlgrp && fcb->vln))
+ if (unlikely(priv->vlgrp && (fcb->flags & RXFCB_VLN)))
ret = gfar_rx_vlan(skb, priv->vlgrp, fcb->vlctl);
else
ret = RECEIVE(skb);
spin_lock_irqsave(&priv->lock, flags);
if (phydev->link) {
u32 tempval = gfar_read(®s->maccfg2);
+ u32 ecntrl = gfar_read(®s->ecntrl);
/* Now we make sure that we can be in full duplex mode.
* If not, we operate in half-duplex mode. */
case 10:
tempval =
((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
+
+ /* Reduced mode distinguishes
+ * between 10 and 100 */
+ if (phydev->speed == SPEED_100)
+ ecntrl |= ECNTRL_R100;
+ else
+ ecntrl &= ~(ECNTRL_R100);
break;
default:
if (netif_msg_link(priv))
}
gfar_write(®s->maccfg2, tempval);
+ gfar_write(®s->ecntrl, ecntrl);
if (!priv->oldlink) {
new_state = 1;
gfar_write(®s->gaddr6, 0xffffffff);
gfar_write(®s->gaddr7, 0xffffffff);
} else {
+ int em_num;
+ int idx;
+
/* zero out the hash */
gfar_write(®s->igaddr0, 0x0);
gfar_write(®s->igaddr1, 0x0);
gfar_write(®s->gaddr6, 0x0);
gfar_write(®s->gaddr7, 0x0);
+ /* If we have extended hash tables, we need to
+ * clear the exact match registers to prepare for
+ * setting them */
+ if (priv->extended_hash) {
+ em_num = GFAR_EM_NUM + 1;
+ gfar_clear_exact_match(dev);
+ idx = 1;
+ } else {
+ idx = 0;
+ em_num = 0;
+ }
+
if(dev->mc_count == 0)
return;
/* Parse the list, and set the appropriate bits */
for(mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {
- gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);
+ if (idx < em_num) {
+ gfar_set_mac_for_addr(dev, idx,
+ mc_ptr->dmi_addr);
+ idx++;
+ } else
+ gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);
}
}
return;
}
+
+/* Clears each of the exact match registers to zero, so they
+ * don't interfere with normal reception */
+static void gfar_clear_exact_match(struct net_device *dev)
+{
+ int idx;
+ u8 zero_arr[MAC_ADDR_LEN] = {0,0,0,0,0,0};
+
+ for(idx = 1;idx < GFAR_EM_NUM + 1;idx++)
+ gfar_set_mac_for_addr(dev, idx, (u8 *)zero_arr);
+}
+
/* Set the appropriate hash bit for the given addr */
/* The algorithm works like so:
* 1) Take the Destination Address (ie the multicast address), and
return;
}
+
+/* There are multiple MAC Address register pairs on some controllers
+ * This function sets the numth pair to a given address
+ */
+static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int idx;
+ char tmpbuf[MAC_ADDR_LEN];
+ u32 tempval;
+ u32 *macptr = &priv->regs->macstnaddr1;
+
+ macptr += num*2;
+
+ /* Now copy it into the mac registers backwards, cuz */
+ /* little endian is silly */
+ for (idx = 0; idx < MAC_ADDR_LEN; idx++)
+ tmpbuf[MAC_ADDR_LEN - 1 - idx] = addr[idx];
+
+ gfar_write(macptr, *((u32 *) (tmpbuf)));
+
+ tempval = *((u32 *) (tmpbuf + 4));
+
+ gfar_write(macptr+1, tempval);
+}
+
/* GFAR error interrupt handler */
static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs)
{
#define GFAR_RX_MAX_RING_SIZE 256
#define GFAR_TX_MAX_RING_SIZE 256
+#define GFAR_MAX_FIFO_THRESHOLD 511
+#define GFAR_MAX_FIFO_STARVE 511
+#define GFAR_MAX_FIFO_STARVE_OFF 511
+
#define DEFAULT_RX_BUFFER_SIZE 1536
#define TX_RING_MOD_MASK(size) (size-1)
#define RX_RING_MOD_MASK(size) (size-1)
#define JUMBO_BUFFER_SIZE 9728
#define JUMBO_FRAME_SIZE 9600
+#define DEFAULT_FIFO_TX_THR 0x100
+#define DEFAULT_FIFO_TX_STARVE 0x40
+#define DEFAULT_FIFO_TX_STARVE_OFF 0x80
+#define DEFAULT_BD_STASH 1
+#define DEFAULT_STASH_LENGTH 64
+#define DEFAULT_STASH_INDEX 0
+
+/* The number of Exact Match registers */
+#define GFAR_EM_NUM 15
+
/* Latency of interface clock in nanoseconds */
/* Interface clock latency , in this case, means the
* time described by a value of 1 in the interrupt
#define DEFAULT_TX_COALESCE 1
#define DEFAULT_TXCOUNT 16
-#define DEFAULT_TXTIME 400
+#define DEFAULT_TXTIME 4
#define DEFAULT_RX_COALESCE 1
#define DEFAULT_RXCOUNT 16
-#define DEFAULT_RXTIME 400
+#define DEFAULT_RXTIME 4
#define TBIPA_VALUE 0x1f
#define MIIMCFG_INIT_VALUE 0x00000007
#define ECNTRL_INIT_SETTINGS 0x00001000
#define ECNTRL_TBI_MODE 0x00000020
+#define ECNTRL_R100 0x00000008
#define MRBLR_INIT_SETTINGS DEFAULT_RX_BUFFER_SIZE
#define RCTRL_PRSDEP_MASK 0x000000c0
#define RCTRL_PRSDEP_INIT 0x000000c0
#define RCTRL_PROM 0x00000008
+#define RCTRL_EMEN 0x00000002
#define RCTRL_CHECKSUMMING (RCTRL_IPCSEN \
| RCTRL_TUCSEN | RCTRL_PRSDEP_INIT)
#define RCTRL_EXTHASH (RCTRL_GHTX)
#define RCTRL_VLAN (RCTRL_PRSDEP_INIT)
+#define RCTRL_PADDING(x) ((x << 16) & RCTRL_PAL_MASK)
#define RSTAT_CLEAR_RHALT 0x00800000
IMASK_XFUN | IMASK_RXC | IMASK_BABT | IMASK_DPE \
| IMASK_PERR)
+/* Fifo management */
+#define FIFO_TX_THR_MASK 0x01ff
+#define FIFO_TX_STARVE_MASK 0x01ff
+#define FIFO_TX_STARVE_OFF_MASK 0x01ff
/* Attribute fields */
/* This enables rx snooping for buffers and descriptors */
-#ifdef CONFIG_GFAR_BDSTASH
#define ATTR_BDSTASH 0x00000800
-#else
-#define ATTR_BDSTASH 0x00000000
-#endif
-#ifdef CONFIG_GFAR_BUFSTASH
#define ATTR_BUFSTASH 0x00004000
-#define STASH_LENGTH 64
-#else
-#define ATTR_BUFSTASH 0x00000000
-#endif
#define ATTR_SNOOPING 0x000000c0
-#define ATTR_INIT_SETTINGS (ATTR_SNOOPING \
- | ATTR_BDSTASH | ATTR_BUFSTASH)
+#define ATTR_INIT_SETTINGS ATTR_SNOOPING
#define ATTRELI_INIT_SETTINGS 0x0
+#define ATTRELI_EL_MASK 0x3fff0000
+#define ATTRELI_EL(x) (x << 16)
+#define ATTRELI_EI_MASK 0x00003fff
+#define ATTRELI_EI(x) (x)
/* TxBD status field bits */
#define RXFCB_CTU 0x0400
#define RXFCB_EIP 0x0200
#define RXFCB_ETU 0x0100
+#define RXFCB_CSUM_MASK 0x0f00
#define RXFCB_PERR_MASK 0x000c
#define RXFCB_PERR_BADL3 0x0008
};
struct txfcb {
- u8 vln:1,
- ip:1,
- ip6:1,
- tup:1,
- udp:1,
- cip:1,
- ctu:1,
- nph:1;
+ u8 flags;
u8 reserved;
u8 l4os; /* Level 4 Header Offset */
u8 l3os; /* Level 3 Header Offset */
};
struct rxfcb {
- u16 vln:1,
- ip:1,
- ip6:1,
- tup:1,
- cip:1,
- ctu:1,
- eip:1,
- etu:1;
+ u16 flags;
u8 rq; /* Receive Queue index */
u8 pro; /* Layer 4 Protocol */
u16 reserved;
spinlock_t lock;
unsigned int rx_buffer_size;
unsigned int rx_stash_size;
+ unsigned int rx_stash_index;
unsigned int tx_ring_size;
unsigned int rx_ring_size;
+ unsigned int fifo_threshold;
+ unsigned int fifo_starve;
+ unsigned int fifo_starve_off;
unsigned char vlan_enable:1,
rx_csum_enable:1,
- extended_hash:1;
+ extended_hash:1,
+ bd_stash_en:1;
unsigned short padding;
struct vlan_group *vlgrp;
/* Info structure initialized by board setup code */
extern void gfar_halt(struct net_device *dev);
extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
int enable, u32 regnum, u32 read);
-void gfar_setup_stashing(struct net_device *dev);
+void gfar_init_sysfs(struct net_device *dev);
#endif /* __GIANFAR_H */
static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
{
struct gfar_private *priv = netdev_priv(dev);
-
+
if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_RMON)
memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
else
#define MII_READ_COMMAND 0x00000001
#define GFAR_SUPPORTED (SUPPORTED_10baseT_Half \
+ | SUPPORTED_10baseT_Full \
| SUPPORTED_100baseT_Half \
| SUPPORTED_100baseT_Full \
| SUPPORTED_Autoneg \
--- /dev/null
+/*
+ * drivers/net/gianfar_sysfs.c
+ *
+ * Gianfar Ethernet Driver
+ * This driver is designed for the non-CPM ethernet controllers
+ * on the 85xx and 83xx family of integrated processors
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2005 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Sysfs file creation and management
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include "gianfar.h"
+
+#define GFAR_ATTR(_name) \
+static ssize_t gfar_show_##_name(struct class_device *cdev, char *buf); \
+static ssize_t gfar_set_##_name(struct class_device *cdev, \
+ const char *buf, size_t count); \
+static CLASS_DEVICE_ATTR(_name, 0644, gfar_show_##_name, gfar_set_##_name)
+
+#define GFAR_CREATE_FILE(_dev, _name) \
+ class_device_create_file(&_dev->class_dev, &class_device_attr_##_name)
+
+GFAR_ATTR(bd_stash);
+GFAR_ATTR(rx_stash_size);
+GFAR_ATTR(rx_stash_index);
+GFAR_ATTR(fifo_threshold);
+GFAR_ATTR(fifo_starve);
+GFAR_ATTR(fifo_starve_off);
+
+#define to_net_dev(cd) container_of(cd, struct net_device, class_dev)
+
+static ssize_t gfar_show_bd_stash(struct class_device *cdev, char *buf)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return sprintf(buf, "%s\n", priv->bd_stash_en? "on" : "off");
+}
+
+static ssize_t gfar_set_bd_stash(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+ int new_setting = 0;
+ u32 temp;
+ unsigned long flags;
+
+ /* Find out the new setting */
+ if (!strncmp("on", buf, count-1) || !strncmp("1", buf, count-1))
+ new_setting = 1;
+ else if (!strncmp("off", buf, count-1) || !strncmp("0", buf, count-1))
+ new_setting = 0;
+ else
+ return count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* Set the new stashing value */
+ priv->bd_stash_en = new_setting;
+
+ temp = gfar_read(&priv->regs->attr);
+
+ if (new_setting)
+ temp |= ATTR_BDSTASH;
+ else
+ temp &= ~(ATTR_BDSTASH);
+
+ gfar_write(&priv->regs->attr, temp);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return count;
+}
+
+static ssize_t gfar_show_rx_stash_size(struct class_device *cdev, char *buf)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return sprintf(buf, "%d\n", priv->rx_stash_size);
+}
+
+static ssize_t gfar_set_rx_stash_size(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned int length = simple_strtoul(buf, NULL, 0);
+ u32 temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (length > priv->rx_buffer_size)
+ return count;
+
+ if (length == priv->rx_stash_size)
+ return count;
+
+ priv->rx_stash_size = length;
+
+ temp = gfar_read(&priv->regs->attreli);
+ temp &= ~ATTRELI_EL_MASK;
+ temp |= ATTRELI_EL(length);
+ gfar_write(&priv->regs->attreli, temp);
+
+ /* Turn stashing on/off as appropriate */
+ temp = gfar_read(&priv->regs->attr);
+
+ if (length)
+ temp |= ATTR_BUFSTASH;
+ else
+ temp &= ~(ATTR_BUFSTASH);
+
+ gfar_write(&priv->regs->attr, temp);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return count;
+}
+
+
+/* Stashing will only be enabled when rx_stash_size != 0 */
+static ssize_t gfar_show_rx_stash_index(struct class_device *cdev, char *buf)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return sprintf(buf, "%d\n", priv->rx_stash_index);
+}
+
+static ssize_t gfar_set_rx_stash_index(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned short index = simple_strtoul(buf, NULL, 0);
+ u32 temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (index > priv->rx_stash_size)
+ return count;
+
+ if (index == priv->rx_stash_index)
+ return count;
+
+ priv->rx_stash_index = index;
+
+ temp = gfar_read(&priv->regs->attreli);
+ temp &= ~ATTRELI_EI_MASK;
+ temp |= ATTRELI_EI(index);
+ gfar_write(&priv->regs->attreli, flags);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return count;
+}
+
+static ssize_t gfar_show_fifo_threshold(struct class_device *cdev, char *buf)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return sprintf(buf, "%d\n", priv->fifo_threshold);
+}
+
+static ssize_t gfar_set_fifo_threshold(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned int length = simple_strtoul(buf, NULL, 0);
+ u32 temp;
+ unsigned long flags;
+
+ if (length > GFAR_MAX_FIFO_THRESHOLD)
+ return count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ priv->fifo_threshold = length;
+
+ temp = gfar_read(&priv->regs->fifo_tx_thr);
+ temp &= ~FIFO_TX_THR_MASK;
+ temp |= length;
+ gfar_write(&priv->regs->fifo_tx_thr, temp);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return count;
+}
+
+static ssize_t gfar_show_fifo_starve(struct class_device *cdev, char *buf)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return sprintf(buf, "%d\n", priv->fifo_starve);
+}
+
+
+static ssize_t gfar_set_fifo_starve(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned int num = simple_strtoul(buf, NULL, 0);
+ u32 temp;
+ unsigned long flags;
+
+ if (num > GFAR_MAX_FIFO_STARVE)
+ return count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ priv->fifo_starve = num;
+
+ temp = gfar_read(&priv->regs->fifo_tx_starve);
+ temp &= ~FIFO_TX_STARVE_MASK;
+ temp |= num;
+ gfar_write(&priv->regs->fifo_tx_starve, temp);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return count;
+}
+
+static ssize_t gfar_show_fifo_starve_off(struct class_device *cdev, char *buf)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return sprintf(buf, "%d\n", priv->fifo_starve_off);
+}
+
+static ssize_t gfar_set_fifo_starve_off(struct class_device *cdev,
+ const char *buf, size_t count)
+{
+ struct net_device *dev = to_net_dev(cdev);
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned int num = simple_strtoul(buf, NULL, 0);
+ u32 temp;
+ unsigned long flags;
+
+ if (num > GFAR_MAX_FIFO_STARVE_OFF)
+ return count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ priv->fifo_starve_off = num;
+
+ temp = gfar_read(&priv->regs->fifo_tx_starve_shutoff);
+ temp &= ~FIFO_TX_STARVE_OFF_MASK;
+ temp |= num;
+ gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return count;
+}
+
+void gfar_init_sysfs(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Initialize the default values */
+ priv->rx_stash_size = DEFAULT_STASH_LENGTH;
+ priv->rx_stash_index = DEFAULT_STASH_INDEX;
+ priv->fifo_threshold = DEFAULT_FIFO_TX_THR;
+ priv->fifo_starve = DEFAULT_FIFO_TX_STARVE;
+ priv->fifo_starve_off = DEFAULT_FIFO_TX_STARVE_OFF;
+ priv->bd_stash_en = DEFAULT_BD_STASH;
+
+ /* Create our sysfs files */
+ GFAR_CREATE_FILE(dev, bd_stash);
+ GFAR_CREATE_FILE(dev, rx_stash_size);
+ GFAR_CREATE_FILE(dev, rx_stash_index);
+ GFAR_CREATE_FILE(dev, fifo_threshold);
+ GFAR_CREATE_FILE(dev, fifo_starve);
+ GFAR_CREATE_FILE(dev, fifo_starve_off);
+
+}
--- /dev/null
+config ENP2611_MSF_NET
+ tristate "Radisys ENP2611 MSF network interface support"
+ depends on ARCH_ENP2611
+ help
+ This is a driver for the MSF network interface unit in
+ the IXP2400 on the Radisys ENP2611 platform.
--- /dev/null
+obj-$(CONFIG_ENP2611_MSF_NET) += enp2611_mod.o
+
+enp2611_mod-objs := caleb.o enp2611.o ixp2400-msf.o ixpdev.o pm3386.o
--- /dev/null
+/*
+ * Helper functions for the SPI-3 bridge FPGA on the Radisys ENP2611
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#define CALEB_IDLO 0x00
+#define CALEB_IDHI 0x01
+#define CALEB_RID 0x02
+#define CALEB_RESET 0x03
+#define CALEB_INTREN0 0x04
+#define CALEB_INTREN1 0x05
+#define CALEB_INTRSTAT0 0x06
+#define CALEB_INTRSTAT1 0x07
+#define CALEB_PORTEN 0x08
+#define CALEB_BURST 0x09
+#define CALEB_PORTPAUS 0x0A
+#define CALEB_PORTPAUSD 0x0B
+#define CALEB_PHY0RX 0x10
+#define CALEB_PHY1RX 0x11
+#define CALEB_PHY0TX 0x12
+#define CALEB_PHY1TX 0x13
+#define CALEB_IXPRX_HI_CNTR 0x15
+#define CALEB_PHY0RX_HI_CNTR 0x16
+#define CALEB_PHY1RX_HI_CNTR 0x17
+#define CALEB_IXPRX_CNTR 0x18
+#define CALEB_PHY0RX_CNTR 0x19
+#define CALEB_PHY1RX_CNTR 0x1A
+#define CALEB_IXPTX_CNTR 0x1B
+#define CALEB_PHY0TX_CNTR 0x1C
+#define CALEB_PHY1TX_CNTR 0x1D
+#define CALEB_DEBUG0 0x1E
+#define CALEB_DEBUG1 0x1F
+
+
+static u8 caleb_reg_read(int reg)
+{
+ u8 value;
+
+ value = *((volatile u8 *)(ENP2611_CALEB_VIRT_BASE + reg));
+
+// printk(KERN_INFO "caleb_reg_read(%d) = %.2x\n", reg, value);
+
+ return value;
+}
+
+static void caleb_reg_write(int reg, u8 value)
+{
+ u8 dummy;
+
+// printk(KERN_INFO "caleb_reg_write(%d, %.2x)\n", reg, value);
+
+ *((volatile u8 *)(ENP2611_CALEB_VIRT_BASE + reg)) = value;
+
+ dummy = *((volatile u8 *)ENP2611_CALEB_VIRT_BASE);
+ __asm__ __volatile__("mov %0, %0" : "+r" (dummy));
+}
+
+
+void caleb_reset(void)
+{
+ /*
+ * Perform a chip reset.
+ */
+ caleb_reg_write(CALEB_RESET, 0x02);
+ udelay(1);
+
+ /*
+ * Enable all interrupt sources. This is needed to get
+ * meaningful results out of the status bits (register 6
+ * and 7.)
+ */
+ caleb_reg_write(CALEB_INTREN0, 0xff);
+ caleb_reg_write(CALEB_INTREN1, 0x07);
+
+ /*
+ * Set RX and TX FIFO thresholds to 1.5kb.
+ */
+ caleb_reg_write(CALEB_PHY0RX, 0x11);
+ caleb_reg_write(CALEB_PHY1RX, 0x11);
+ caleb_reg_write(CALEB_PHY0TX, 0x11);
+ caleb_reg_write(CALEB_PHY1TX, 0x11);
+
+ /*
+ * Program SPI-3 burst size.
+ */
+ caleb_reg_write(CALEB_BURST, 0); // 64-byte RBUF mpackets
+// caleb_reg_write(CALEB_BURST, 1); // 128-byte RBUF mpackets
+// caleb_reg_write(CALEB_BURST, 2); // 256-byte RBUF mpackets
+}
+
+void caleb_enable_rx(int port)
+{
+ u8 temp;
+
+ temp = caleb_reg_read(CALEB_PORTEN);
+ temp |= 1 << port;
+ caleb_reg_write(CALEB_PORTEN, temp);
+}
+
+void caleb_disable_rx(int port)
+{
+ u8 temp;
+
+ temp = caleb_reg_read(CALEB_PORTEN);
+ temp &= ~(1 << port);
+ caleb_reg_write(CALEB_PORTEN, temp);
+}
+
+void caleb_enable_tx(int port)
+{
+ u8 temp;
+
+ temp = caleb_reg_read(CALEB_PORTEN);
+ temp |= 1 << (port + 4);
+ caleb_reg_write(CALEB_PORTEN, temp);
+}
+
+void caleb_disable_tx(int port)
+{
+ u8 temp;
+
+ temp = caleb_reg_read(CALEB_PORTEN);
+ temp &= ~(1 << (port + 4));
+ caleb_reg_write(CALEB_PORTEN, temp);
+}
--- /dev/null
+/*
+ * Helper functions for the SPI-3 bridge FPGA on the Radisys ENP2611
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __CALEB_H
+#define __CALEB_H
+
+void caleb_reset(void);
+void caleb_enable_rx(int port);
+void caleb_disable_rx(int port);
+void caleb_enable_tx(int port);
+void caleb_disable_tx(int port);
+
+
+#endif
--- /dev/null
+/*
+ * IXP2400 MSF network device driver for the Radisys ENP2611
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <asm/arch/uengine.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include "ixpdev.h"
+#include "caleb.h"
+#include "ixp2400-msf.h"
+#include "pm3386.h"
+
+/***********************************************************************
+ * The Radisys ENP2611 is a PCI form factor board with three SFP GBIC
+ * slots, connected via two PMC/Sierra 3386s and an SPI-3 bridge FPGA
+ * to the IXP2400.
+ *
+ * +-------------+
+ * SFP GBIC #0 ---+ | +---------+
+ * | PM3386 #0 +-------+ |
+ * SFP GBIC #1 ---+ | | "Caleb" | +---------+
+ * +-------------+ | | | |
+ * | SPI-3 +---------+ IXP2400 |
+ * +-------------+ | bridge | | |
+ * SFP GBIC #2 ---+ | | FPGA | +---------+
+ * | PM3386 #1 +-------+ |
+ * | | +---------+
+ * +-------------+
+ * ^ ^ ^
+ * | 1.25Gbaud | 104MHz | 104MHz
+ * | SERDES ea. | SPI-3 ea. | SPI-3
+ *
+ ***********************************************************************/
+static struct ixp2400_msf_parameters enp2611_msf_parameters =
+{
+ .rx_mode = IXP2400_RX_MODE_UTOPIA_POS |
+ IXP2400_RX_MODE_1x32 |
+ IXP2400_RX_MODE_MPHY |
+ IXP2400_RX_MODE_MPHY_32 |
+ IXP2400_RX_MODE_MPHY_POLLED_STATUS |
+ IXP2400_RX_MODE_MPHY_LEVEL3 |
+ IXP2400_RX_MODE_RBUF_SIZE_64,
+
+ .rxclk01_multiplier = IXP2400_PLL_MULTIPLIER_16,
+
+ .rx_poll_ports = 3,
+
+ .rx_channel_mode = {
+ IXP2400_PORT_RX_MODE_MASTER |
+ IXP2400_PORT_RX_MODE_POS_PHY |
+ IXP2400_PORT_RX_MODE_POS_PHY_L3 |
+ IXP2400_PORT_RX_MODE_ODD_PARITY |
+ IXP2400_PORT_RX_MODE_2_CYCLE_DECODE,
+
+ IXP2400_PORT_RX_MODE_MASTER |
+ IXP2400_PORT_RX_MODE_POS_PHY |
+ IXP2400_PORT_RX_MODE_POS_PHY_L3 |
+ IXP2400_PORT_RX_MODE_ODD_PARITY |
+ IXP2400_PORT_RX_MODE_2_CYCLE_DECODE,
+
+ IXP2400_PORT_RX_MODE_MASTER |
+ IXP2400_PORT_RX_MODE_POS_PHY |
+ IXP2400_PORT_RX_MODE_POS_PHY_L3 |
+ IXP2400_PORT_RX_MODE_ODD_PARITY |
+ IXP2400_PORT_RX_MODE_2_CYCLE_DECODE,
+
+ IXP2400_PORT_RX_MODE_MASTER |
+ IXP2400_PORT_RX_MODE_POS_PHY |
+ IXP2400_PORT_RX_MODE_POS_PHY_L3 |
+ IXP2400_PORT_RX_MODE_ODD_PARITY |
+ IXP2400_PORT_RX_MODE_2_CYCLE_DECODE
+ },
+
+ .tx_mode = IXP2400_TX_MODE_UTOPIA_POS |
+ IXP2400_TX_MODE_1x32 |
+ IXP2400_TX_MODE_MPHY |
+ IXP2400_TX_MODE_MPHY_32 |
+ IXP2400_TX_MODE_MPHY_POLLED_STATUS |
+ IXP2400_TX_MODE_MPHY_LEVEL3 |
+ IXP2400_TX_MODE_TBUF_SIZE_64,
+
+ .txclk01_multiplier = IXP2400_PLL_MULTIPLIER_16,
+
+ .tx_poll_ports = 3,
+
+ .tx_channel_mode = {
+ IXP2400_PORT_TX_MODE_MASTER |
+ IXP2400_PORT_TX_MODE_POS_PHY |
+ IXP2400_PORT_TX_MODE_ODD_PARITY |
+ IXP2400_PORT_TX_MODE_2_CYCLE_DECODE,
+
+ IXP2400_PORT_TX_MODE_MASTER |
+ IXP2400_PORT_TX_MODE_POS_PHY |
+ IXP2400_PORT_TX_MODE_ODD_PARITY |
+ IXP2400_PORT_TX_MODE_2_CYCLE_DECODE,
+
+ IXP2400_PORT_TX_MODE_MASTER |
+ IXP2400_PORT_TX_MODE_POS_PHY |
+ IXP2400_PORT_TX_MODE_ODD_PARITY |
+ IXP2400_PORT_TX_MODE_2_CYCLE_DECODE,
+
+ IXP2400_PORT_TX_MODE_MASTER |
+ IXP2400_PORT_TX_MODE_POS_PHY |
+ IXP2400_PORT_TX_MODE_ODD_PARITY |
+ IXP2400_PORT_TX_MODE_2_CYCLE_DECODE
+ }
+};
+
+struct enp2611_ixpdev_priv
+{
+ struct ixpdev_priv ixpdev_priv;
+ struct net_device_stats stats;
+};
+
+static struct net_device *nds[3];
+static struct timer_list link_check_timer;
+
+static struct net_device_stats *enp2611_get_stats(struct net_device *dev)
+{
+ struct enp2611_ixpdev_priv *ip = netdev_priv(dev);
+
+ pm3386_get_stats(ip->ixpdev_priv.channel, &(ip->stats));
+
+ return &(ip->stats);
+}
+
+/* @@@ Poll the SFP moddef0 line too. */
+/* @@@ Try to use the pm3386 DOOL interrupt as well. */
+static void enp2611_check_link_status(unsigned long __dummy)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ struct net_device *dev;
+ int status;
+
+ if (!netif_running(nds[i]))
+ continue;
+
+ dev = nds[i];
+
+ status = pm3386_is_link_up(i);
+ if (status && !netif_carrier_ok(nds[i])) {
+ pm3386_enable_tx(i);
+ caleb_enable_tx(i);
+ netif_carrier_on(nds[i]);
+ } else if (!status && netif_carrier_ok(nds[i])) {
+ netif_carrier_off(nds[i]);
+ caleb_disable_tx(i);
+ pm3386_disable_tx(i);
+ }
+ }
+
+ link_check_timer.expires = jiffies + HZ / 10;
+ add_timer(&link_check_timer);
+}
+
+static void enp2611_set_port_admin_status(int port, int up)
+{
+ if (up) {
+ caleb_enable_rx(port);
+ pm3386_enable_rx(port);
+ } else {
+ caleb_disable_tx(port);
+ pm3386_disable_tx(port);
+ pm3386_disable_rx(port);
+ caleb_disable_rx(port);
+ }
+}
+
+static int __init enp2611_init_module(void)
+{
+ int i;
+
+ if (!machine_is_enp2611())
+ return -ENODEV;
+
+ caleb_reset();
+ pm3386_reset();
+
+ for (i = 0; i < 3; i++) {
+ nds[i] = ixpdev_alloc(i, sizeof(struct enp2611_ixpdev_priv));
+ if (nds[i] == NULL) {
+ while (--i >= 0)
+ free_netdev(nds[i]);
+ return -ENOMEM;
+ }
+
+ SET_MODULE_OWNER(nds[i]);
+ nds[i]->get_stats = enp2611_get_stats;
+ pm3386_init_port(i);
+ pm3386_get_mac(i, nds[i]->dev_addr);
+ }
+
+ ixp2400_msf_init(&enp2611_msf_parameters);
+
+ if (ixpdev_init(3, nds, enp2611_set_port_admin_status)) {
+ for (i = 0; i < 3; i++)
+ free_netdev(nds[i]);
+ return -EINVAL;
+ }
+
+ init_timer(&link_check_timer);
+ link_check_timer.function = enp2611_check_link_status;
+ link_check_timer.expires = jiffies;
+ add_timer(&link_check_timer);
+
+ return 0;
+}
+
+static void __exit enp2611_cleanup_module(void)
+{
+ int i;
+
+ del_timer_sync(&link_check_timer);
+
+ ixpdev_deinit();
+ for (i = 0; i < 3; i++)
+ free_netdev(nds[i]);
+}
+
+module_init(enp2611_init_module);
+module_exit(enp2611_cleanup_module);
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Generic library functions for the MSF (Media and Switch Fabric) unit
+ * found on the Intel IXP2400 network processor.
+ *
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/hardware.h>
+#include <asm/arch/ixp2000-regs.h>
+#include <asm/delay.h>
+#include <asm/io.h>
+#include "ixp2400-msf.h"
+
+/*
+ * This is the Intel recommended PLL init procedure as described on
+ * page 340 of the IXP2400/IXP2800 Programmer's Reference Manual.
+ */
+static void ixp2400_pll_init(struct ixp2400_msf_parameters *mp)
+{
+ int rx_dual_clock;
+ int tx_dual_clock;
+ u32 value;
+
+ /*
+ * If the RX mode is not 1x32, we have to enable both RX PLLs
+ * (#0 and #1.) The same thing for the TX direction.
+ */
+ rx_dual_clock = !!(mp->rx_mode & IXP2400_RX_MODE_WIDTH_MASK);
+ tx_dual_clock = !!(mp->tx_mode & IXP2400_TX_MODE_WIDTH_MASK);
+
+ /*
+ * Read initial value.
+ */
+ value = ixp2000_reg_read(IXP2000_MSF_CLK_CNTRL);
+
+ /*
+ * Put PLLs in powerdown and bypass mode.
+ */
+ value |= 0x0000f0f0;
+ ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
+
+ /*
+ * Set single or dual clock mode bits.
+ */
+ value &= ~0x03000000;
+ value |= (rx_dual_clock << 24) | (tx_dual_clock << 25);
+
+ /*
+ * Set multipliers.
+ */
+ value &= ~0x00ff0000;
+ value |= mp->rxclk01_multiplier << 16;
+ value |= mp->rxclk23_multiplier << 18;
+ value |= mp->txclk01_multiplier << 20;
+ value |= mp->txclk23_multiplier << 22;
+
+ /*
+ * And write value.
+ */
+ ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
+
+ /*
+ * Disable PLL bypass mode.
+ */
+ value &= ~(0x00005000 | rx_dual_clock << 13 | tx_dual_clock << 15);
+ ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
+
+ /*
+ * Turn on PLLs.
+ */
+ value &= ~(0x00000050 | rx_dual_clock << 5 | tx_dual_clock << 7);
+ ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value);
+
+ /*
+ * Wait for PLLs to lock. There are lock status bits, but IXP2400
+ * erratum #65 says that these lock bits should not be relied upon
+ * as they might not accurately reflect the true state of the PLLs.
+ */
+ udelay(100);
+}
+
+/*
+ * Needed according to p480 of Programmer's Reference Manual.
+ */
+static void ixp2400_msf_free_rbuf_entries(struct ixp2400_msf_parameters *mp)
+{
+ int size_bits;
+ int i;
+
+ /*
+ * Work around IXP2400 erratum #69 (silent RBUF-to-DRAM transfer
+ * corruption) in the Intel-recommended way: do not add the RBUF
+ * elements susceptible to corruption to the freelist.
+ */
+ size_bits = mp->rx_mode & IXP2400_RX_MODE_RBUF_SIZE_MASK;
+ if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_64) {
+ for (i = 1; i < 128; i++) {
+ if (i == 9 || i == 18 || i == 27)
+ continue;
+ ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i);
+ }
+ } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_128) {
+ for (i = 1; i < 64; i++) {
+ if (i == 4 || i == 9 || i == 13)
+ continue;
+ ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i);
+ }
+ } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_256) {
+ for (i = 1; i < 32; i++) {
+ if (i == 2 || i == 4 || i == 6)
+ continue;
+ ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i);
+ }
+ }
+}
+
+static u32 ixp2400_msf_valid_channels(u32 reg)
+{
+ u32 channels;
+
+ channels = 0;
+ switch (reg & IXP2400_RX_MODE_WIDTH_MASK) {
+ case IXP2400_RX_MODE_1x32:
+ channels = 0x1;
+ if (reg & IXP2400_RX_MODE_MPHY &&
+ !(reg & IXP2400_RX_MODE_MPHY_32))
+ channels = 0xf;
+ break;
+
+ case IXP2400_RX_MODE_2x16:
+ channels = 0x5;
+ break;
+
+ case IXP2400_RX_MODE_4x8:
+ channels = 0xf;
+ break;
+
+ case IXP2400_RX_MODE_1x16_2x8:
+ channels = 0xd;
+ break;
+ }
+
+ return channels;
+}
+
+static void ixp2400_msf_enable_rx(struct ixp2400_msf_parameters *mp)
+{
+ u32 value;
+
+ value = ixp2000_reg_read(IXP2000_MSF_RX_CONTROL) & 0x0fffffff;
+ value |= ixp2400_msf_valid_channels(mp->rx_mode) << 28;
+ ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, value);
+}
+
+static void ixp2400_msf_enable_tx(struct ixp2400_msf_parameters *mp)
+{
+ u32 value;
+
+ value = ixp2000_reg_read(IXP2000_MSF_TX_CONTROL) & 0x0fffffff;
+ value |= ixp2400_msf_valid_channels(mp->tx_mode) << 28;
+ ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, value);
+}
+
+
+void ixp2400_msf_init(struct ixp2400_msf_parameters *mp)
+{
+ u32 value;
+ int i;
+
+ /*
+ * Init the RX/TX PLLs based on the passed parameter block.
+ */
+ ixp2400_pll_init(mp);
+
+ /*
+ * Reset MSF. Bit 7 in IXP_RESET_0 resets the MSF.
+ */
+ value = ixp2000_reg_read(IXP2000_RESET0);
+ ixp2000_reg_write(IXP2000_RESET0, value | 0x80);
+ ixp2000_reg_write(IXP2000_RESET0, value & ~0x80);
+
+ /*
+ * Initialise the RX section.
+ */
+ ixp2000_reg_write(IXP2000_MSF_RX_MPHY_POLL_LIMIT, mp->rx_poll_ports - 1);
+ ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, mp->rx_mode);
+ for (i = 0; i < 4; i++) {
+ ixp2000_reg_write(IXP2000_MSF_RX_UP_CONTROL_0 + i,
+ mp->rx_channel_mode[i]);
+ }
+ ixp2400_msf_free_rbuf_entries(mp);
+ ixp2400_msf_enable_rx(mp);
+
+ /*
+ * Initialise the TX section.
+ */
+ ixp2000_reg_write(IXP2000_MSF_TX_MPHY_POLL_LIMIT, mp->tx_poll_ports - 1);
+ ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, mp->tx_mode);
+ for (i = 0; i < 4; i++) {
+ ixp2000_reg_write(IXP2000_MSF_TX_UP_CONTROL_0 + i,
+ mp->tx_channel_mode[i]);
+ }
+ ixp2400_msf_enable_tx(mp);
+}
--- /dev/null
+/*
+ * Generic library functions for the MSF (Media and Switch Fabric) unit
+ * found on the Intel IXP2400 network processor.
+ *
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ */
+
+#ifndef __IXP2400_MSF_H
+#define __IXP2400_MSF_H
+
+struct ixp2400_msf_parameters
+{
+ u32 rx_mode;
+ unsigned rxclk01_multiplier:2;
+ unsigned rxclk23_multiplier:2;
+ unsigned rx_poll_ports:6;
+ u32 rx_channel_mode[4];
+
+ u32 tx_mode;
+ unsigned txclk01_multiplier:2;
+ unsigned txclk23_multiplier:2;
+ unsigned tx_poll_ports:6;
+ u32 tx_channel_mode[4];
+};
+
+void ixp2400_msf_init(struct ixp2400_msf_parameters *mp);
+
+#define IXP2400_PLL_MULTIPLIER_48 0x00
+#define IXP2400_PLL_MULTIPLIER_24 0x01
+#define IXP2400_PLL_MULTIPLIER_16 0x02
+#define IXP2400_PLL_MULTIPLIER_12 0x03
+
+#define IXP2400_RX_MODE_CSIX 0x00400000
+#define IXP2400_RX_MODE_UTOPIA_POS 0x00000000
+#define IXP2400_RX_MODE_WIDTH_MASK 0x00300000
+#define IXP2400_RX_MODE_1x16_2x8 0x00300000
+#define IXP2400_RX_MODE_4x8 0x00200000
+#define IXP2400_RX_MODE_2x16 0x00100000
+#define IXP2400_RX_MODE_1x32 0x00000000
+#define IXP2400_RX_MODE_MPHY 0x00080000
+#define IXP2400_RX_MODE_SPHY 0x00000000
+#define IXP2400_RX_MODE_MPHY_32 0x00040000
+#define IXP2400_RX_MODE_MPHY_4 0x00000000
+#define IXP2400_RX_MODE_MPHY_POLLED_STATUS 0x00020000
+#define IXP2400_RX_MODE_MPHY_DIRECT_STATUS 0x00000000
+#define IXP2400_RX_MODE_CBUS_FULL_DUPLEX 0x00010000
+#define IXP2400_RX_MODE_CBUS_SIMPLEX 0x00000000
+#define IXP2400_RX_MODE_MPHY_LEVEL2 0x00004000
+#define IXP2400_RX_MODE_MPHY_LEVEL3 0x00000000
+#define IXP2400_RX_MODE_CBUS_8BIT 0x00002000
+#define IXP2400_RX_MODE_CBUS_4BIT 0x00000000
+#define IXP2400_RX_MODE_CSIX_SINGLE_FREELIST 0x00000200
+#define IXP2400_RX_MODE_CSIX_SPLIT_FREELISTS 0x00000000
+#define IXP2400_RX_MODE_RBUF_SIZE_MASK 0x0000000c
+#define IXP2400_RX_MODE_RBUF_SIZE_256 0x00000008
+#define IXP2400_RX_MODE_RBUF_SIZE_128 0x00000004
+#define IXP2400_RX_MODE_RBUF_SIZE_64 0x00000000
+
+#define IXP2400_PORT_RX_MODE_SLAVE 0x00000040
+#define IXP2400_PORT_RX_MODE_MASTER 0x00000000
+#define IXP2400_PORT_RX_MODE_POS_PHY_L3 0x00000020
+#define IXP2400_PORT_RX_MODE_POS_PHY_L2 0x00000000
+#define IXP2400_PORT_RX_MODE_POS_PHY 0x00000010
+#define IXP2400_PORT_RX_MODE_UTOPIA 0x00000000
+#define IXP2400_PORT_RX_MODE_EVEN_PARITY 0x0000000c
+#define IXP2400_PORT_RX_MODE_ODD_PARITY 0x00000008
+#define IXP2400_PORT_RX_MODE_NO_PARITY 0x00000000
+#define IXP2400_PORT_RX_MODE_UTOPIA_BIG_CELLS 0x00000002
+#define IXP2400_PORT_RX_MODE_UTOPIA_NORMAL_CELLS 0x00000000
+#define IXP2400_PORT_RX_MODE_2_CYCLE_DECODE 0x00000001
+#define IXP2400_PORT_RX_MODE_1_CYCLE_DECODE 0x00000000
+
+#define IXP2400_TX_MODE_CSIX 0x00400000
+#define IXP2400_TX_MODE_UTOPIA_POS 0x00000000
+#define IXP2400_TX_MODE_WIDTH_MASK 0x00300000
+#define IXP2400_TX_MODE_1x16_2x8 0x00300000
+#define IXP2400_TX_MODE_4x8 0x00200000
+#define IXP2400_TX_MODE_2x16 0x00100000
+#define IXP2400_TX_MODE_1x32 0x00000000
+#define IXP2400_TX_MODE_MPHY 0x00080000
+#define IXP2400_TX_MODE_SPHY 0x00000000
+#define IXP2400_TX_MODE_MPHY_32 0x00040000
+#define IXP2400_TX_MODE_MPHY_4 0x00000000
+#define IXP2400_TX_MODE_MPHY_POLLED_STATUS 0x00020000
+#define IXP2400_TX_MODE_MPHY_DIRECT_STATUS 0x00000000
+#define IXP2400_TX_MODE_CBUS_FULL_DUPLEX 0x00010000
+#define IXP2400_TX_MODE_CBUS_SIMPLEX 0x00000000
+#define IXP2400_TX_MODE_MPHY_LEVEL2 0x00004000
+#define IXP2400_TX_MODE_MPHY_LEVEL3 0x00000000
+#define IXP2400_TX_MODE_CBUS_8BIT 0x00002000
+#define IXP2400_TX_MODE_CBUS_4BIT 0x00000000
+#define IXP2400_TX_MODE_TBUF_SIZE_MASK 0x0000000c
+#define IXP2400_TX_MODE_TBUF_SIZE_256 0x00000008
+#define IXP2400_TX_MODE_TBUF_SIZE_128 0x00000004
+#define IXP2400_TX_MODE_TBUF_SIZE_64 0x00000000
+
+#define IXP2400_PORT_TX_MODE_SLAVE 0x00000040
+#define IXP2400_PORT_TX_MODE_MASTER 0x00000000
+#define IXP2400_PORT_TX_MODE_POS_PHY 0x00000010
+#define IXP2400_PORT_TX_MODE_UTOPIA 0x00000000
+#define IXP2400_PORT_TX_MODE_EVEN_PARITY 0x0000000c
+#define IXP2400_PORT_TX_MODE_ODD_PARITY 0x00000008
+#define IXP2400_PORT_TX_MODE_NO_PARITY 0x00000000
+#define IXP2400_PORT_TX_MODE_UTOPIA_BIG_CELLS 0x00000002
+#define IXP2400_PORT_TX_MODE_2_CYCLE_DECODE 0x00000001
+#define IXP2400_PORT_TX_MODE_1_CYCLE_DECODE 0x00000000
+
+
+#endif
--- /dev/null
+/*
+ * RX ucode for the Intel IXP2400 in POS-PHY mode.
+ * Copyright (C) 2004, 2005 Lennert Buytenhek
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Assumptions made in this code:
+ * - The IXP2400 MSF is configured for POS-PHY mode, in a mode where
+ * only one full element list is used. This includes, for example,
+ * 1x32 SPHY and 1x32 MPHY32, but not 4x8 SPHY or 1x32 MPHY4. (This
+ * is not an exhaustive list.)
+ * - The RBUF uses 64-byte mpackets.
+ * - RX descriptors reside in SRAM, and have the following format:
+ * struct rx_desc
+ * {
+ * // to uengine
+ * u32 buf_phys_addr;
+ * u32 buf_length;
+ *
+ * // from uengine
+ * u32 channel;
+ * u32 pkt_length;
+ * };
+ * - Packet data resides in DRAM.
+ * - Packet buffer addresses are 8-byte aligned.
+ * - Scratch ring 0 is rx_pending.
+ * - Scratch ring 1 is rx_done, and has status condition 'full'.
+ * - The host triggers rx_done flush and rx_pending refill on seeing INTA.
+ * - This code is run on all eight threads of the microengine it runs on.
+ *
+ * Local memory is used for per-channel RX state.
+ */
+
+#define RX_THREAD_FREELIST_0 0x0030
+#define RBUF_ELEMENT_DONE 0x0044
+
+#define CHANNEL_FLAGS *l$index0[0]
+#define CHANNEL_FLAG_RECEIVING 1
+#define PACKET_LENGTH *l$index0[1]
+#define PACKET_CHECKSUM *l$index0[2]
+#define BUFFER_HANDLE *l$index0[3]
+#define BUFFER_START *l$index0[4]
+#define BUFFER_LENGTH *l$index0[5]
+
+#define CHANNEL_STATE_SIZE 24 // in bytes
+#define CHANNEL_STATE_SHIFT 5 // ceil(log2(state size))
+
+
+ .sig volatile sig1
+ .sig volatile sig2
+ .sig volatile sig3
+
+ .sig mpacket_arrived
+ .reg add_to_rx_freelist
+ .reg read $rsw0, $rsw1
+ .xfer_order $rsw0 $rsw1
+
+ .reg zero
+
+ /*
+ * Initialise add_to_rx_freelist.
+ */
+ .begin
+ .reg temp
+ .reg temp2
+
+ immed[add_to_rx_freelist, RX_THREAD_FREELIST_0]
+ immed_w1[add_to_rx_freelist, (&$rsw0 | (&mpacket_arrived << 12))]
+
+ local_csr_rd[ACTIVE_CTX_STS]
+ immed[temp, 0]
+ alu[temp2, temp, and, 0x1f]
+ alu_shf[add_to_rx_freelist, add_to_rx_freelist, or, temp2, <<20]
+ alu[temp2, temp, and, 0x80]
+ alu_shf[add_to_rx_freelist, add_to_rx_freelist, or, temp2, <<18]
+ .end
+
+ immed[zero, 0]
+
+ /*
+ * Skip context 0 initialisation?
+ */
+ .begin
+ br!=ctx[0, mpacket_receive_loop#]
+ .end
+
+ /*
+ * Initialise local memory.
+ */
+ .begin
+ .reg addr
+ .reg temp
+
+ immed[temp, 0]
+ init_local_mem_loop#:
+ alu_shf[addr, --, b, temp, <<CHANNEL_STATE_SHIFT]
+ local_csr_wr[ACTIVE_LM_ADDR_0, addr]
+ nop
+ nop
+ nop
+
+ immed[CHANNEL_FLAGS, 0]
+
+ alu[temp, temp, +, 1]
+ alu[--, temp, and, 0x20]
+ beq[init_local_mem_loop#]
+ .end
+
+ /*
+ * Initialise signal pipeline.
+ */
+ .begin
+ local_csr_wr[SAME_ME_SIGNAL, (&sig1 << 3)]
+ .set_sig sig1
+
+ local_csr_wr[SAME_ME_SIGNAL, (&sig2 << 3)]
+ .set_sig sig2
+
+ local_csr_wr[SAME_ME_SIGNAL, (&sig3 << 3)]
+ .set_sig sig3
+ .end
+
+mpacket_receive_loop#:
+ /*
+ * Synchronise and wait for mpacket.
+ */
+ .begin
+ ctx_arb[sig1]
+ local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig1 << 3))]
+
+ msf[fast_wr, --, add_to_rx_freelist, 0]
+ .set_sig mpacket_arrived
+ ctx_arb[mpacket_arrived]
+ .set $rsw0 $rsw1
+ .end
+
+ /*
+ * We halt if we see {inbparerr,parerr,null,soperror}.
+ */
+ .begin
+ alu_shf[--, 0x1b, and, $rsw0, >>8]
+ bne[abort_rswerr#]
+ .end
+
+ /*
+ * Point local memory pointer to this channel's state area.
+ */
+ .begin
+ .reg chanaddr
+
+ alu[chanaddr, $rsw0, and, 0x1f]
+ alu_shf[chanaddr, --, b, chanaddr, <<CHANNEL_STATE_SHIFT]
+ local_csr_wr[ACTIVE_LM_ADDR_0, chanaddr]
+ nop
+ nop
+ nop
+ .end
+
+ /*
+ * Check whether we received a SOP mpacket while we were already
+ * working on a packet, or a non-SOP mpacket while there was no
+ * packet pending. (SOP == RECEIVING -> abort) If everything's
+ * okay, update the RECEIVING flag to reflect our new state.
+ */
+ .begin
+ .reg temp
+ .reg eop
+
+ #if CHANNEL_FLAG_RECEIVING != 1
+ #error CHANNEL_FLAG_RECEIVING is not 1
+ #endif
+
+ alu_shf[temp, 1, and, $rsw0, >>15]
+ alu[temp, temp, xor, CHANNEL_FLAGS]
+ alu[--, temp, and, CHANNEL_FLAG_RECEIVING]
+ beq[abort_proterr#]
+
+ alu_shf[eop, 1, and, $rsw0, >>14]
+ alu[CHANNEL_FLAGS, temp, xor, eop]
+ .end
+
+ /*
+ * Copy the mpacket into the right spot, and in case of EOP,
+ * write back the descriptor and pass the packet on.
+ */
+ .begin
+ .reg buffer_offset
+ .reg _packet_length
+ .reg _packet_checksum
+ .reg _buffer_handle
+ .reg _buffer_start
+ .reg _buffer_length
+
+ /*
+ * Determine buffer_offset, _packet_length and
+ * _packet_checksum.
+ */
+ .begin
+ .reg temp
+
+ alu[--, 1, and, $rsw0, >>15]
+ beq[not_sop#]
+
+ immed[PACKET_LENGTH, 0]
+ immed[PACKET_CHECKSUM, 0]
+
+ not_sop#:
+ alu[buffer_offset, --, b, PACKET_LENGTH]
+ alu_shf[temp, 0xff, and, $rsw0, >>16]
+ alu[_packet_length, buffer_offset, +, temp]
+ alu[PACKET_LENGTH, --, b, _packet_length]
+
+ immed[temp, 0xffff]
+ alu[temp, $rsw1, and, temp]
+ alu[_packet_checksum, PACKET_CHECKSUM, +, temp]
+ alu[PACKET_CHECKSUM, --, b, _packet_checksum]
+ .end
+
+ /*
+ * Allocate buffer in case of SOP.
+ */
+ .begin
+ .reg temp
+
+ alu[temp, 1, and, $rsw0, >>15]
+ beq[skip_buffer_alloc#]
+
+ .begin
+ .sig zzz
+ .reg read $stemp $stemp2
+ .xfer_order $stemp $stemp2
+
+ rx_nobufs#:
+ scratch[get, $stemp, zero, 0, 1], ctx_swap[zzz]
+ alu[_buffer_handle, --, b, $stemp]
+ beq[rx_nobufs#]
+
+ sram[read, $stemp, _buffer_handle, 0, 2],
+ ctx_swap[zzz]
+ alu[_buffer_start, --, b, $stemp]
+ alu[_buffer_length, --, b, $stemp2]
+ .end
+
+ skip_buffer_alloc#:
+ .end
+
+ /*
+ * Resynchronise.
+ */
+ .begin
+ ctx_arb[sig2]
+ local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig2 << 3))]
+ .end
+
+ /*
+ * Synchronise buffer state.
+ */
+ .begin
+ .reg temp
+
+ alu[temp, 1, and, $rsw0, >>15]
+ beq[copy_from_local_mem#]
+
+ alu[BUFFER_HANDLE, --, b, _buffer_handle]
+ alu[BUFFER_START, --, b, _buffer_start]
+ alu[BUFFER_LENGTH, --, b, _buffer_length]
+ br[sync_state_done#]
+
+ copy_from_local_mem#:
+ alu[_buffer_handle, --, b, BUFFER_HANDLE]
+ alu[_buffer_start, --, b, BUFFER_START]
+ alu[_buffer_length, --, b, BUFFER_LENGTH]
+
+ sync_state_done#:
+ .end
+
+#if 0
+ /*
+ * Debug buffer state management.
+ */
+ .begin
+ .reg temp
+
+ alu[temp, 1, and, $rsw0, >>14]
+ beq[no_poison#]
+ immed[BUFFER_HANDLE, 0xdead]
+ immed[BUFFER_START, 0xdead]
+ immed[BUFFER_LENGTH, 0xdead]
+ no_poison#:
+
+ immed[temp, 0xdead]
+ alu[--, _buffer_handle, -, temp]
+ beq[state_corrupted#]
+ alu[--, _buffer_start, -, temp]
+ beq[state_corrupted#]
+ alu[--, _buffer_length, -, temp]
+ beq[state_corrupted#]
+ .end
+#endif
+
+ /*
+ * Check buffer length.
+ */
+ .begin
+ alu[--, _buffer_length, -, _packet_length]
+ blo[buffer_overflow#]
+ .end
+
+ /*
+ * Copy the mpacket and give back the RBUF element.
+ */
+ .begin
+ .reg element
+ .reg xfer_size
+ .reg temp
+ .sig copy_sig
+
+ alu_shf[element, 0x7f, and, $rsw0, >>24]
+ alu_shf[xfer_size, 0xff, and, $rsw0, >>16]
+
+ alu[xfer_size, xfer_size, -, 1]
+ alu_shf[xfer_size, 0x10, or, xfer_size, >>3]
+ alu_shf[temp, 0x10, or, xfer_size, <<21]
+ alu_shf[temp, temp, or, element, <<11]
+ alu_shf[--, temp, or, 1, <<18]
+
+ dram[rbuf_rd, --, _buffer_start, buffer_offset, max_8],
+ indirect_ref, sig_done[copy_sig]
+ ctx_arb[copy_sig]
+
+ alu[temp, RBUF_ELEMENT_DONE, or, element, <<16]
+ msf[fast_wr, --, temp, 0]
+ .end
+
+ /*
+ * If EOP, write back the packet descriptor.
+ */
+ .begin
+ .reg write $stemp $stemp2
+ .xfer_order $stemp $stemp2
+ .sig zzz
+
+ alu_shf[--, 1, and, $rsw0, >>14]
+ beq[no_writeback#]
+
+ alu[$stemp, $rsw0, and, 0x1f]
+ alu[$stemp2, --, b, _packet_length]
+ sram[write, $stemp, _buffer_handle, 8, 2], ctx_swap[zzz]
+
+ no_writeback#:
+ .end
+
+ /*
+ * Resynchronise.
+ */
+ .begin
+ ctx_arb[sig3]
+ local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig3 << 3))]
+ .end
+
+ /*
+ * If EOP, put the buffer back onto the scratch ring.
+ */
+ .begin
+ .reg write $stemp
+ .sig zzz
+
+ br_inp_state[SCR_Ring1_Status, rx_done_ring_overflow#]
+
+ alu_shf[--, 1, and, $rsw0, >>14]
+ beq[mpacket_receive_loop#]
+
+ alu[--, 1, and, $rsw0, >>10]
+ bne[rxerr#]
+
+ alu[$stemp, --, b, _buffer_handle]
+ scratch[put, $stemp, zero, 4, 1], ctx_swap[zzz]
+ cap[fast_wr, 0, XSCALE_INT_A]
+ br[mpacket_receive_loop#]
+
+ rxerr#:
+ alu[$stemp, --, b, _buffer_handle]
+ scratch[put, $stemp, zero, 0, 1], ctx_swap[zzz]
+ br[mpacket_receive_loop#]
+ .end
+ .end
+
+
+abort_rswerr#:
+ halt
+
+abort_proterr#:
+ halt
+
+state_corrupted#:
+ halt
+
+buffer_overflow#:
+ halt
+
+rx_done_ring_overflow#:
+ halt
+
+
--- /dev/null
+static struct ixp2000_uengine_code ixp2400_rx =
+{
+ .cpu_model_bitmask = 0x000003fe,
+ .cpu_min_revision = 0,
+ .cpu_max_revision = 255,
+
+ .uengine_parameters = IXP2000_UENGINE_8_CONTEXTS |
+ IXP2000_UENGINE_PRN_UPDATE_EVERY |
+ IXP2000_UENGINE_NN_FROM_PREVIOUS |
+ IXP2000_UENGINE_ASSERT_EMPTY_AT_0 |
+ IXP2000_UENGINE_LM_ADDR1_PER_CONTEXT |
+ IXP2000_UENGINE_LM_ADDR0_PER_CONTEXT,
+
+ .initial_reg_values = (struct ixp2000_reg_value []) {
+ { -1, -1 }
+ },
+
+ .num_insns = 109,
+ .insns = (u8 []) {
+ 0xf0, 0x00, 0x0c, 0xc0, 0x05,
+ 0xf4, 0x44, 0x0c, 0x00, 0x05,
+ 0xfc, 0x04, 0x4c, 0x00, 0x00,
+ 0xf0, 0x00, 0x00, 0x3b, 0x00,
+ 0xb4, 0x40, 0xf0, 0x3b, 0x1f,
+ 0x8a, 0xc0, 0x50, 0x3e, 0x05,
+ 0xb4, 0x40, 0xf0, 0x3b, 0x80,
+ 0x9a, 0xe0, 0x00, 0x3e, 0x05,
+ 0xf0, 0x00, 0x00, 0x07, 0x00,
+ 0xd8, 0x05, 0xc0, 0x00, 0x11,
+ 0xf0, 0x00, 0x00, 0x0f, 0x00,
+ 0x91, 0xb0, 0x20, 0x0e, 0x00,
+ 0xfc, 0x06, 0x60, 0x0b, 0x00,
+ 0xf0, 0x00, 0x0c, 0x03, 0x00,
+ 0xf0, 0x00, 0x0c, 0x03, 0x00,
+ 0xf0, 0x00, 0x0c, 0x03, 0x00,
+ 0xf0, 0x00, 0x0c, 0x02, 0x00,
+ 0xb0, 0xc0, 0x30, 0x0f, 0x01,
+ 0xa4, 0x70, 0x00, 0x0f, 0x20,
+ 0xd8, 0x02, 0xc0, 0x01, 0x00,
+ 0xfc, 0x10, 0xac, 0x23, 0x08,
+ 0xfc, 0x10, 0xac, 0x43, 0x10,
+ 0xfc, 0x10, 0xac, 0x63, 0x18,
+ 0xe0, 0x00, 0x00, 0x00, 0x02,
+ 0xfc, 0x10, 0xae, 0x23, 0x88,
+ 0x3d, 0x00, 0x04, 0x03, 0x20,
+ 0xe0, 0x00, 0x00, 0x00, 0x10,
+ 0x84, 0x82, 0x02, 0x01, 0x3b,
+ 0xd8, 0x1a, 0x00, 0x01, 0x01,
+ 0xb4, 0x00, 0x8c, 0x7d, 0x80,
+ 0x91, 0xb0, 0x80, 0x22, 0x00,
+ 0xfc, 0x06, 0x60, 0x23, 0x00,
+ 0xf0, 0x00, 0x0c, 0x03, 0x00,
+ 0xf0, 0x00, 0x0c, 0x03, 0x00,
+ 0xf0, 0x00, 0x0c, 0x03, 0x00,
+ 0x94, 0xf0, 0x92, 0x01, 0x21,
+ 0xac, 0x40, 0x60, 0x26, 0x00,
+ 0xa4, 0x30, 0x0c, 0x04, 0x06,
+ 0xd8, 0x1a, 0x40, 0x01, 0x00,
+ 0x94, 0xe0, 0xa2, 0x01, 0x21,
+ 0xac, 0x20, 0x00, 0x28, 0x06,
+ 0x84, 0xf2, 0x02, 0x01, 0x21,
+ 0xd8, 0x0b, 0x40, 0x01, 0x00,
+ 0xf0, 0x00, 0x0c, 0x02, 0x01,
+ 0xf0, 0x00, 0x0c, 0x02, 0x02,
+ 0xa0, 0x00, 0x08, 0x04, 0x00,
+ 0x95, 0x00, 0xc6, 0x01, 0xff,
+ 0xa0, 0x80, 0x10, 0x30, 0x00,
+ 0xa0, 0x60, 0x1c, 0x00, 0x01,
+ 0xf0, 0x0f, 0xf0, 0x33, 0xff,
+ 0xb4, 0x00, 0xc0, 0x31, 0x81,
+ 0xb0, 0x80, 0xb0, 0x32, 0x02,
+ 0xa0, 0x20, 0x20, 0x2c, 0x00,
+ 0x94, 0xf0, 0xd2, 0x01, 0x21,
+ 0xd8, 0x0f, 0x40, 0x01, 0x00,
+ 0x19, 0x40, 0x10, 0x04, 0x20,
+ 0xa0, 0x00, 0x26, 0x04, 0x00,
+ 0xd8, 0x0d, 0xc0, 0x01, 0x00,
+ 0x00, 0x42, 0x10, 0x80, 0x02,
+ 0xb0, 0x00, 0x46, 0x04, 0x00,
+ 0xb0, 0x00, 0x56, 0x08, 0x00,
+ 0xe0, 0x00, 0x00, 0x00, 0x04,
+ 0xfc, 0x10, 0xae, 0x43, 0x90,
+ 0x84, 0xf0, 0x32, 0x01, 0x21,
+ 0xd8, 0x11, 0x40, 0x01, 0x00,
+ 0xa0, 0x60, 0x3c, 0x00, 0x02,
+ 0xa0, 0x20, 0x40, 0x10, 0x00,
+ 0xa0, 0x20, 0x50, 0x14, 0x00,
+ 0xd8, 0x12, 0x00, 0x00, 0x18,
+ 0xa0, 0x00, 0x28, 0x0c, 0x00,
+ 0xb0, 0x00, 0x48, 0x10, 0x00,
+ 0xb0, 0x00, 0x58, 0x14, 0x00,
+ 0xaa, 0xf0, 0x00, 0x14, 0x01,
+ 0xd8, 0x1a, 0xc0, 0x01, 0x05,
+ 0x85, 0x80, 0x42, 0x01, 0xff,
+ 0x95, 0x00, 0x66, 0x01, 0xff,
+ 0xba, 0xc0, 0x60, 0x1b, 0x01,
+ 0x9a, 0x30, 0x60, 0x19, 0x30,
+ 0x9a, 0xb0, 0x70, 0x1a, 0x30,
+ 0x9b, 0x50, 0x78, 0x1e, 0x04,
+ 0x8a, 0xe2, 0x08, 0x1e, 0x21,
+ 0x6a, 0x4e, 0x00, 0x13, 0x00,
+ 0xe0, 0x00, 0x00, 0x00, 0x30,
+ 0x9b, 0x00, 0x7a, 0x92, 0x04,
+ 0x3d, 0x00, 0x04, 0x1f, 0x20,
+ 0x84, 0xe2, 0x02, 0x01, 0x21,
+ 0xd8, 0x16, 0x80, 0x01, 0x00,
+ 0xa4, 0x18, 0x0c, 0x7d, 0x80,
+ 0xa0, 0x58, 0x1c, 0x00, 0x01,
+ 0x01, 0x42, 0x00, 0xa0, 0x02,
+ 0xe0, 0x00, 0x00, 0x00, 0x08,
+ 0xfc, 0x10, 0xae, 0x63, 0x98,
+ 0xd8, 0x1b, 0x00, 0xc2, 0x14,
+ 0x84, 0xe2, 0x02, 0x01, 0x21,
+ 0xd8, 0x05, 0xc0, 0x01, 0x00,
+ 0x84, 0xa2, 0x02, 0x01, 0x21,
+ 0xd8, 0x19, 0x40, 0x01, 0x01,
+ 0xa0, 0x58, 0x0c, 0x00, 0x02,
+ 0x1a, 0x40, 0x00, 0x04, 0x24,
+ 0x33, 0x00, 0x01, 0x2f, 0x20,
+ 0xd8, 0x05, 0xc0, 0x00, 0x18,
+ 0xa0, 0x58, 0x0c, 0x00, 0x02,
+ 0x1a, 0x40, 0x00, 0x04, 0x20,
+ 0xd8, 0x05, 0xc0, 0x00, 0x18,
+ 0xe0, 0x00, 0x02, 0x00, 0x00,
+ 0xe0, 0x00, 0x02, 0x00, 0x00,
+ 0xe0, 0x00, 0x02, 0x00, 0x00,
+ 0xe0, 0x00, 0x02, 0x00, 0x00,
+ 0xe0, 0x00, 0x02, 0x00, 0x00,
+ }
+};
--- /dev/null
+/*
+ * TX ucode for the Intel IXP2400 in POS-PHY mode.
+ * Copyright (C) 2004, 2005 Lennert Buytenhek
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Assumptions made in this code:
+ * - The IXP2400 MSF is configured for POS-PHY mode, in a mode where
+ * only one TBUF partition is used. This includes, for example,
+ * 1x32 SPHY and 1x32 MPHY32, but not 4x8 SPHY or 1x32 MPHY4. (This
+ * is not an exhaustive list.)
+ * - The TBUF uses 64-byte mpackets.
+ * - TX descriptors reside in SRAM, and have the following format:
+ * struct tx_desc
+ * {
+ * // to uengine
+ * u32 buf_phys_addr;
+ * u32 pkt_length;
+ * u32 channel;
+ * };
+ * - Packet data resides in DRAM.
+ * - Packet buffer addresses are 8-byte aligned.
+ * - Scratch ring 2 is tx_pending.
+ * - Scratch ring 3 is tx_done, and has status condition 'full'.
+ * - This code is run on all eight threads of the microengine it runs on.
+ */
+
+#define TX_SEQUENCE_0 0x0060
+#define TBUF_CTRL 0x1800
+
+#define PARTITION_SIZE 128
+#define PARTITION_THRESH 96
+
+
+ .sig volatile sig1
+ .sig volatile sig2
+ .sig volatile sig3
+
+ .reg @old_tx_seq_0
+ .reg @mpkts_in_flight
+ .reg @next_tbuf_mpacket
+
+ .reg @buffer_handle
+ .reg @buffer_start
+ .reg @packet_length
+ .reg @channel
+ .reg @packet_offset
+
+ .reg zero
+
+ immed[zero, 0]
+
+ /*
+ * Skip context 0 initialisation?
+ */
+ .begin
+ br!=ctx[0, mpacket_tx_loop#]
+ .end
+
+ /*
+ * Wait until all pending TBUF elements have been transmitted.
+ */
+ .begin
+ .reg read $tx
+ .sig zzz
+
+ loop_empty#:
+ msf[read, $tx, zero, TX_SEQUENCE_0, 1], ctx_swap[zzz]
+ alu_shf[--, --, b, $tx, >>31]
+ beq[loop_empty#]
+
+ alu[@old_tx_seq_0, --, b, $tx]
+ .end
+
+ immed[@mpkts_in_flight, 0]
+ alu[@next_tbuf_mpacket, @old_tx_seq_0, and, (PARTITION_SIZE - 1)]
+
+ immed[@buffer_handle, 0]
+
+ /*
+ * Initialise signal pipeline.
+ */
+ .begin
+ local_csr_wr[SAME_ME_SIGNAL, (&sig1 << 3)]
+ .set_sig sig1
+
+ local_csr_wr[SAME_ME_SIGNAL, (&sig2 << 3)]
+ .set_sig sig2
+
+ local_csr_wr[SAME_ME_SIGNAL, (&sig3 << 3)]
+ .set_sig sig3
+ .end
+
+mpacket_tx_loop#:
+ .begin
+ .reg tbuf_element_index
+ .reg buffer_handle
+ .reg sop_eop
+ .reg packet_data
+ .reg channel
+ .reg mpacket_size
+
+ /*
+ * If there is no packet currently being transmitted,
+ * dequeue the next TX descriptor, and fetch the buffer
+ * address, packet length and destination channel number.
+ */
+ .begin
+ .reg read $stemp $stemp2 $stemp3
+ .xfer_order $stemp $stemp2 $stemp3
+ .sig zzz
+
+ ctx_arb[sig1]
+
+ alu[--, --, b, @buffer_handle]
+ bne[already_got_packet#]
+
+ tx_nobufs#:
+ scratch[get, $stemp, zero, 8, 1], ctx_swap[zzz]
+ alu[@buffer_handle, --, b, $stemp]
+ beq[tx_nobufs#]
+
+ sram[read, $stemp, $stemp, 0, 3], ctx_swap[zzz]
+ alu[@buffer_start, --, b, $stemp]
+ alu[@packet_length, --, b, $stemp2]
+ beq[zero_byte_packet#]
+ alu[@channel, --, b, $stemp3]
+ immed[@packet_offset, 0]
+
+ already_got_packet#:
+ local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig1 << 3))]
+ .end
+
+ /*
+ * Determine tbuf element index, SOP/EOP flags, mpacket
+ * offset and mpacket size and cache buffer_handle and
+ * channel number.
+ */
+ .begin
+ alu[tbuf_element_index, --, b, @next_tbuf_mpacket]
+ alu[@next_tbuf_mpacket, @next_tbuf_mpacket, +, 1]
+ alu[@next_tbuf_mpacket, @next_tbuf_mpacket, and,
+ (PARTITION_SIZE - 1)]
+
+ alu[buffer_handle, --, b, @buffer_handle]
+ immed[@buffer_handle, 0]
+
+ immed[sop_eop, 1]
+
+ alu[packet_data, --, b, @packet_offset]
+ bne[no_sop#]
+ alu[sop_eop, sop_eop, or, 2]
+ no_sop#:
+ alu[packet_data, packet_data, +, @buffer_start]
+
+ alu[channel, --, b, @channel]
+
+ alu[mpacket_size, @packet_length, -, @packet_offset]
+ alu[--, 64, -, mpacket_size]
+ bhs[eop#]
+ alu[@buffer_handle, --, b, buffer_handle]
+ immed[mpacket_size, 64]
+ alu[sop_eop, sop_eop, and, 2]
+ eop#:
+
+ alu[@packet_offset, @packet_offset, +, mpacket_size]
+ .end
+
+ /*
+ * Wait until there's enough space in the TBUF.
+ */
+ .begin
+ .reg read $tx
+ .reg temp
+ .sig zzz
+
+ ctx_arb[sig2]
+
+ br[test_space#]
+
+ loop_space#:
+ msf[read, $tx, zero, TX_SEQUENCE_0, 1], ctx_swap[zzz]
+
+ alu[temp, $tx, -, @old_tx_seq_0]
+ alu[temp, temp, and, 0xff]
+ alu[@mpkts_in_flight, @mpkts_in_flight, -, temp]
+
+ alu[@old_tx_seq_0, --, b, $tx]
+
+ test_space#:
+ alu[--, PARTITION_THRESH, -, @mpkts_in_flight]
+ blo[loop_space#]
+
+ alu[@mpkts_in_flight, @mpkts_in_flight, +, 1]
+
+ local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig2 << 3))]
+ .end
+
+ /*
+ * Copy the packet data to the TBUF.
+ */
+ .begin
+ .reg temp
+ .sig copy_sig
+
+ alu[temp, mpacket_size, -, 1]
+ alu_shf[temp, 0x10, or, temp, >>3]
+ alu_shf[temp, 0x10, or, temp, <<21]
+ alu_shf[temp, temp, or, tbuf_element_index, <<11]
+ alu_shf[--, temp, or, 1, <<18]
+
+ dram[tbuf_wr, --, packet_data, 0, max_8],
+ indirect_ref, sig_done[copy_sig]
+ ctx_arb[copy_sig]
+ .end
+
+ /*
+ * Mark TBUF element as ready-to-be-transmitted.
+ */
+ .begin
+ .reg write $tsw $tsw2
+ .xfer_order $tsw $tsw2
+ .reg temp
+ .sig zzz
+
+ alu_shf[temp, channel, or, mpacket_size, <<24]
+ alu_shf[$tsw, temp, or, sop_eop, <<8]
+ immed[$tsw2, 0]
+
+ immed[temp, TBUF_CTRL]
+ alu_shf[temp, temp, or, tbuf_element_index, <<3]
+ msf[write, $tsw, temp, 0, 2], ctx_swap[zzz]
+ .end
+
+ /*
+ * Resynchronise.
+ */
+ .begin
+ ctx_arb[sig3]
+ local_csr_wr[SAME_ME_SIGNAL, (0x80 | (&sig3 << 3))]
+ .end
+
+ /*
+ * If this was an EOP mpacket, recycle the TX buffer
+ * and signal the host.
+ */
+ .begin
+ .reg write $stemp
+ .sig zzz
+
+ alu[--, sop_eop, and, 1]
+ beq[mpacket_tx_loop#]
+
+ tx_done_ring_full#:
+ br_inp_state[SCR_Ring3_Status, tx_done_ring_full#]
+
+ alu[$stemp, --, b, buffer_handle]
+ scratch[put, $stemp, zero, 12, 1], ctx_swap[zzz]
+ cap[fast_wr, 0, XSCALE_INT_A]
+ br[mpacket_tx_loop#]
+ .end
+ .end
+
+
+zero_byte_packet#:
+ halt
+
+
--- /dev/null
+static struct ixp2000_uengine_code ixp2400_tx =
+{
+ .cpu_model_bitmask = 0x000003fe,
+ .cpu_min_revision = 0,
+ .cpu_max_revision = 255,
+
+ .uengine_parameters = IXP2000_UENGINE_8_CONTEXTS |
+ IXP2000_UENGINE_PRN_UPDATE_EVERY |
+ IXP2000_UENGINE_NN_FROM_PREVIOUS |
+ IXP2000_UENGINE_ASSERT_EMPTY_AT_0 |
+ IXP2000_UENGINE_LM_ADDR1_PER_CONTEXT |
+ IXP2000_UENGINE_LM_ADDR0_PER_CONTEXT,
+
+ .initial_reg_values = (struct ixp2000_reg_value []) {
+ { -1, -1 }
+ },
+
+ .num_insns = 77,
+ .insns = (u8 []) {
+ 0xf0, 0x00, 0x00, 0x07, 0x00,
+ 0xd8, 0x03, 0x00, 0x00, 0x11,
+ 0x3c, 0x40, 0x00, 0x04, 0xe0,
+ 0x81, 0xf2, 0x02, 0x01, 0x00,
+ 0xd8, 0x00, 0x80, 0x01, 0x00,
+ 0xb0, 0x08, 0x06, 0x00, 0x00,
+ 0xf0, 0x00, 0x0c, 0x00, 0x80,
+ 0xb4, 0x49, 0x02, 0x03, 0x7f,
+ 0xf0, 0x00, 0x02, 0x83, 0x00,
+ 0xfc, 0x10, 0xac, 0x23, 0x08,
+ 0xfc, 0x10, 0xac, 0x43, 0x10,
+ 0xfc, 0x10, 0xac, 0x63, 0x18,
+ 0xe0, 0x00, 0x00, 0x00, 0x02,
+ 0xa0, 0x30, 0x02, 0x80, 0x00,
+ 0xd8, 0x06, 0x00, 0x01, 0x01,
+ 0x19, 0x40, 0x00, 0x04, 0x28,
+ 0xb0, 0x0a, 0x06, 0x00, 0x00,
+ 0xd8, 0x03, 0xc0, 0x01, 0x00,
+ 0x00, 0x44, 0x00, 0x80, 0x80,
+ 0xa0, 0x09, 0x06, 0x00, 0x00,
+ 0xb0, 0x0b, 0x06, 0x04, 0x00,
+ 0xd8, 0x13, 0x00, 0x01, 0x00,
+ 0xb0, 0x0c, 0x06, 0x08, 0x00,
+ 0xf0, 0x00, 0x0c, 0x00, 0xa0,
+ 0xfc, 0x10, 0xae, 0x23, 0x88,
+ 0xa0, 0x00, 0x12, 0x40, 0x00,
+ 0xb0, 0xc9, 0x02, 0x43, 0x01,
+ 0xb4, 0x49, 0x02, 0x43, 0x7f,
+ 0xb0, 0x00, 0x22, 0x80, 0x00,
+ 0xf0, 0x00, 0x02, 0x83, 0x00,
+ 0xf0, 0x00, 0x0c, 0x04, 0x02,
+ 0xb0, 0x40, 0x6c, 0x00, 0xa0,
+ 0xd8, 0x08, 0x80, 0x01, 0x01,
+ 0xaa, 0x00, 0x2c, 0x08, 0x02,
+ 0xa0, 0xc0, 0x30, 0x18, 0x90,
+ 0xa0, 0x00, 0x43, 0x00, 0x00,
+ 0xba, 0xc0, 0x32, 0xc0, 0xa0,
+ 0xaa, 0xb0, 0x00, 0x0f, 0x40,
+ 0xd8, 0x0a, 0x80, 0x01, 0x04,
+ 0xb0, 0x0a, 0x00, 0x08, 0x00,
+ 0xf0, 0x00, 0x00, 0x0f, 0x40,
+ 0xa4, 0x00, 0x2c, 0x08, 0x02,
+ 0xa0, 0x8a, 0x00, 0x0c, 0xa0,
+ 0xe0, 0x00, 0x00, 0x00, 0x04,
+ 0xd8, 0x0c, 0x80, 0x00, 0x18,
+ 0x3c, 0x40, 0x00, 0x04, 0xe0,
+ 0xba, 0x80, 0x42, 0x01, 0x80,
+ 0xb4, 0x40, 0x40, 0x13, 0xff,
+ 0xaa, 0x88, 0x00, 0x10, 0x80,
+ 0xb0, 0x08, 0x06, 0x00, 0x00,
+ 0xaa, 0xf0, 0x0d, 0x80, 0x80,
+ 0xd8, 0x0b, 0x40, 0x01, 0x05,
+ 0xa0, 0x88, 0x0c, 0x04, 0x80,
+ 0xfc, 0x10, 0xae, 0x43, 0x90,
+ 0xba, 0xc0, 0x50, 0x0f, 0x01,
+ 0x9a, 0x30, 0x50, 0x15, 0x30,
+ 0x9a, 0xb0, 0x50, 0x16, 0x30,
+ 0x9b, 0x50, 0x58, 0x16, 0x01,
+ 0x8a, 0xe2, 0x08, 0x16, 0x21,
+ 0x6b, 0x4e, 0x00, 0x83, 0x03,
+ 0xe0, 0x00, 0x00, 0x00, 0x30,
+ 0x9a, 0x80, 0x70, 0x0e, 0x04,
+ 0x8b, 0x88, 0x08, 0x1e, 0x02,
+ 0xf0, 0x00, 0x0c, 0x01, 0x81,
+ 0xf0, 0x01, 0x80, 0x1f, 0x00,
+ 0x9b, 0xd0, 0x78, 0x1e, 0x01,
+ 0x3d, 0x42, 0x00, 0x1c, 0x20,
+ 0xe0, 0x00, 0x00, 0x00, 0x08,
+ 0xfc, 0x10, 0xae, 0x63, 0x98,
+ 0xa4, 0x30, 0x0c, 0x04, 0x02,
+ 0xd8, 0x03, 0x00, 0x01, 0x00,
+ 0xd8, 0x11, 0xc1, 0x42, 0x14,
+ 0xa0, 0x18, 0x00, 0x08, 0x00,
+ 0x1a, 0x40, 0x00, 0x04, 0x2c,
+ 0x33, 0x00, 0x01, 0x2f, 0x20,
+ 0xd8, 0x03, 0x00, 0x00, 0x18,
+ 0xe0, 0x00, 0x02, 0x00, 0x00,
+ }
+};
--- /dev/null
+/*
+ * IXP2000 MSF network device driver
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <asm/arch/uengine.h>
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include "ixp2400_rx.ucode"
+#include "ixp2400_tx.ucode"
+#include "ixpdev_priv.h"
+#include "ixpdev.h"
+
+static int nds_count;
+static struct net_device **nds;
+static int nds_open;
+static void (*set_port_admin_status)(int port, int up);
+
+static struct ixpdev_rx_desc * const rx_desc =
+ (struct ixpdev_rx_desc *)(IXP2000_SRAM0_VIRT_BASE + RX_BUF_DESC_BASE);
+static struct ixpdev_tx_desc * const tx_desc =
+ (struct ixpdev_tx_desc *)(IXP2000_SRAM0_VIRT_BASE + TX_BUF_DESC_BASE);
+static int tx_pointer;
+
+
+static int ixpdev_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ixpdev_priv *ip = netdev_priv(dev);
+ struct ixpdev_tx_desc *desc;
+ int entry;
+
+ if (unlikely(skb->len > PAGE_SIZE)) {
+ /* @@@ Count drops. */
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ entry = tx_pointer;
+ tx_pointer = (tx_pointer + 1) % TX_BUF_COUNT;
+
+ desc = tx_desc + entry;
+ desc->pkt_length = skb->len;
+ desc->channel = ip->channel;
+
+ skb_copy_and_csum_dev(skb, phys_to_virt(desc->buf_addr));
+ dev_kfree_skb(skb);
+
+ ixp2000_reg_write(RING_TX_PENDING,
+ TX_BUF_DESC_BASE + (entry * sizeof(struct ixpdev_tx_desc)));
+
+ dev->trans_start = jiffies;
+
+ local_irq_disable();
+ ip->tx_queue_entries++;
+ if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
+ netif_stop_queue(dev);
+ local_irq_enable();
+
+ return 0;
+}
+
+
+static int ixpdev_rx(struct net_device *dev, int *budget)
+{
+ while (*budget > 0) {
+ struct ixpdev_rx_desc *desc;
+ struct sk_buff *skb;
+ void *buf;
+ u32 _desc;
+
+ _desc = ixp2000_reg_read(RING_RX_DONE);
+ if (_desc == 0)
+ return 0;
+
+ desc = rx_desc +
+ ((_desc - RX_BUF_DESC_BASE) / sizeof(struct ixpdev_rx_desc));
+ buf = phys_to_virt(desc->buf_addr);
+
+ if (desc->pkt_length < 4 || desc->pkt_length > PAGE_SIZE) {
+ printk(KERN_ERR "ixp2000: rx err, length %d\n",
+ desc->pkt_length);
+ goto err;
+ }
+
+ if (desc->channel < 0 || desc->channel >= nds_count) {
+ printk(KERN_ERR "ixp2000: rx err, channel %d\n",
+ desc->channel);
+ goto err;
+ }
+
+ /* @@@ Make FCS stripping configurable. */
+ desc->pkt_length -= 4;
+
+ if (unlikely(!netif_running(nds[desc->channel])))
+ goto err;
+
+ skb = dev_alloc_skb(desc->pkt_length + 2);
+ if (likely(skb != NULL)) {
+ skb->dev = nds[desc->channel];
+ skb_reserve(skb, 2);
+ eth_copy_and_sum(skb, buf, desc->pkt_length, 0);
+ skb_put(skb, desc->pkt_length);
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ skb->dev->last_rx = jiffies;
+
+ netif_receive_skb(skb);
+ }
+
+err:
+ ixp2000_reg_write(RING_RX_PENDING, _desc);
+ dev->quota--;
+ (*budget)--;
+ }
+
+ return 1;
+}
+
+/* dev always points to nds[0]. */
+static int ixpdev_poll(struct net_device *dev, int *budget)
+{
+ /* @@@ Have to stop polling when nds[0] is administratively
+ * downed while we are polling. */
+ do {
+ ixp2000_reg_write(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0x00ff);
+
+ if (ixpdev_rx(dev, budget))
+ return 1;
+ } while (ixp2000_reg_read(IXP2000_IRQ_THD_RAW_STATUS_A_0) & 0x00ff);
+
+ netif_rx_complete(dev);
+ ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0x00ff);
+
+ return 0;
+}
+
+/* @@@ Ugly hack. */
+static inline int netif_rx_schedule_prep_notup(struct net_device *dev)
+{
+ return !test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state);
+}
+
+static void ixpdev_tx_complete(void)
+{
+ int channel;
+ u32 wake;
+
+ wake = 0;
+ while (1) {
+ struct ixpdev_priv *ip;
+ u32 desc;
+ int entry;
+
+ desc = ixp2000_reg_read(RING_TX_DONE);
+ if (desc == 0)
+ break;
+
+ /* @@@ Check whether entries come back in order. */
+ entry = (desc - TX_BUF_DESC_BASE) / sizeof(struct ixpdev_tx_desc);
+ channel = tx_desc[entry].channel;
+
+ if (channel < 0 || channel >= nds_count) {
+ printk(KERN_ERR "ixp2000: txcomp channel index "
+ "out of bounds (%d, %.8i, %d)\n",
+ channel, (unsigned int)desc, entry);
+ continue;
+ }
+
+ ip = netdev_priv(nds[channel]);
+ if (ip->tx_queue_entries == TX_BUF_COUNT_PER_CHAN)
+ wake |= 1 << channel;
+ ip->tx_queue_entries--;
+ }
+
+ for (channel = 0; wake != 0; channel++) {
+ if (wake & (1 << channel)) {
+ netif_wake_queue(nds[channel]);
+ wake &= ~(1 << channel);
+ }
+ }
+}
+
+static irqreturn_t ixpdev_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 status;
+
+ status = ixp2000_reg_read(IXP2000_IRQ_THD_STATUS_A_0);
+ if (status == 0)
+ return IRQ_NONE;
+
+ /*
+ * Any of the eight receive units signaled RX?
+ */
+ if (status & 0x00ff) {
+ ixp2000_reg_wrb(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0x00ff);
+ if (likely(netif_rx_schedule_prep_notup(nds[0]))) {
+ __netif_rx_schedule(nds[0]);
+ } else {
+ printk(KERN_CRIT "ixp2000: irq while polling!!\n");
+ }
+ }
+
+ /*
+ * Any of the eight transmit units signaled TXdone?
+ */
+ if (status & 0xff00) {
+ ixp2000_reg_wrb(IXP2000_IRQ_THD_RAW_STATUS_A_0, 0xff00);
+ ixpdev_tx_complete();
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ixpdev_open(struct net_device *dev)
+{
+ struct ixpdev_priv *ip = netdev_priv(dev);
+ int err;
+
+ if (!nds_open++) {
+ err = request_irq(IRQ_IXP2000_THDA0, ixpdev_interrupt,
+ SA_SHIRQ, "ixp2000_eth", nds);
+ if (err) {
+ nds_open--;
+ return err;
+ }
+
+ ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_SET_A_0, 0xffff);
+ }
+
+ set_port_admin_status(ip->channel, 1);
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+static int ixpdev_close(struct net_device *dev)
+{
+ struct ixpdev_priv *ip = netdev_priv(dev);
+
+ netif_stop_queue(dev);
+ set_port_admin_status(ip->channel, 0);
+
+ if (!--nds_open) {
+ ixp2000_reg_write(IXP2000_IRQ_THD_ENABLE_CLEAR_A_0, 0xffff);
+ free_irq(IRQ_IXP2000_THDA0, nds);
+ }
+
+ return 0;
+}
+
+struct net_device *ixpdev_alloc(int channel, int sizeof_priv)
+{
+ struct net_device *dev;
+ struct ixpdev_priv *ip;
+
+ dev = alloc_etherdev(sizeof_priv);
+ if (dev == NULL)
+ return NULL;
+
+ dev->hard_start_xmit = ixpdev_xmit;
+ dev->poll = ixpdev_poll;
+ dev->open = ixpdev_open;
+ dev->stop = ixpdev_close;
+
+ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
+ dev->weight = 64;
+
+ ip = netdev_priv(dev);
+ ip->channel = channel;
+ ip->tx_queue_entries = 0;
+
+ return dev;
+}
+
+int ixpdev_init(int __nds_count, struct net_device **__nds,
+ void (*__set_port_admin_status)(int port, int up))
+{
+ int i;
+ int err;
+
+ if (RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192) {
+ static void __too_many_rx_or_tx_buffers(void);
+ __too_many_rx_or_tx_buffers();
+ }
+
+ nds_count = __nds_count;
+ nds = __nds;
+ set_port_admin_status = __set_port_admin_status;
+
+ for (i = 0; i < nds_count; i++) {
+ err = register_netdev(nds[i]);
+ if (err) {
+ while (--i >= 0)
+ unregister_netdev(nds[i]);
+ goto err_out;
+ }
+ }
+
+ for (i = 0; i < RX_BUF_COUNT; i++) {
+ void *buf;
+
+ buf = (void *)get_zeroed_page(GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ while (--i >= 0)
+ free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
+ goto err_unregister;
+ }
+ rx_desc[i].buf_addr = virt_to_phys(buf);
+ rx_desc[i].buf_length = PAGE_SIZE;
+ }
+
+ /* @@@ Maybe we shouldn't be preallocating TX buffers. */
+ for (i = 0; i < TX_BUF_COUNT; i++) {
+ void *buf;
+
+ buf = (void *)get_zeroed_page(GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ while (--i >= 0)
+ free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
+ goto err_free_rx;
+ }
+ tx_desc[i].buf_addr = virt_to_phys(buf);
+ }
+
+ /* 256 entries, ring status set means 'empty', base address 0x0000. */
+ ixp2000_reg_write(RING_RX_PENDING_BASE, 0x44000000);
+ ixp2000_reg_write(RING_RX_PENDING_HEAD, 0x00000000);
+ ixp2000_reg_write(RING_RX_PENDING_TAIL, 0x00000000);
+
+ /* 256 entries, ring status set means 'full', base address 0x0400. */
+ ixp2000_reg_write(RING_RX_DONE_BASE, 0x40000400);
+ ixp2000_reg_write(RING_RX_DONE_HEAD, 0x00000000);
+ ixp2000_reg_write(RING_RX_DONE_TAIL, 0x00000000);
+
+ for (i = 0; i < RX_BUF_COUNT; i++) {
+ ixp2000_reg_write(RING_RX_PENDING,
+ RX_BUF_DESC_BASE + (i * sizeof(struct ixpdev_rx_desc)));
+ }
+
+ ixp2000_uengine_load(0, &ixp2400_rx);
+ ixp2000_uengine_start_contexts(0, 0xff);
+
+
+ /* 256 entries, ring status set means 'empty', base address 0x0800. */
+ ixp2000_reg_write(RING_TX_PENDING_BASE, 0x44000800);
+ ixp2000_reg_write(RING_TX_PENDING_HEAD, 0x00000000);
+ ixp2000_reg_write(RING_TX_PENDING_TAIL, 0x00000000);
+
+ /* 256 entries, ring status set means 'full', base address 0x0c00. */
+ ixp2000_reg_write(RING_TX_DONE_BASE, 0x40000c00);
+ ixp2000_reg_write(RING_TX_DONE_HEAD, 0x00000000);
+ ixp2000_reg_write(RING_TX_DONE_TAIL, 0x00000000);
+
+ ixp2000_uengine_load(1, &ixp2400_tx);
+ ixp2000_uengine_start_contexts(1, 0xff);
+
+ return 0;
+
+err_free_rx:
+ for (i = 0; i < RX_BUF_COUNT; i++)
+ free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
+
+err_unregister:
+ for (i = 0; i < nds_count; i++)
+ unregister_netdev(nds[i]);
+
+err_out:
+ return err;
+}
+
+void ixpdev_deinit(void)
+{
+ int i;
+
+ /* @@@ Flush out pending packets. */
+
+ ixp2000_uengine_stop_contexts(1, 0xff);
+ ixp2000_uengine_stop_contexts(0, 0xff);
+ ixp2000_uengine_reset(0x3);
+
+ for (i = 0; i < TX_BUF_COUNT; i++)
+ free_page((unsigned long)phys_to_virt(tx_desc[i].buf_addr));
+
+ for (i = 0; i < RX_BUF_COUNT; i++)
+ free_page((unsigned long)phys_to_virt(rx_desc[i].buf_addr));
+
+ for (i = 0; i < nds_count; i++)
+ unregister_netdev(nds[i]);
+}
--- /dev/null
+/*
+ * IXP2000 MSF network device driver
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __IXPDEV_H
+#define __IXPDEV_H
+
+struct ixpdev_priv
+{
+ int channel;
+ int tx_queue_entries;
+};
+
+struct net_device *ixpdev_alloc(int channel, int sizeof_priv);
+int ixpdev_init(int num_ports, struct net_device **nds,
+ void (*set_port_admin_status)(int port, int up));
+void ixpdev_deinit(void);
+
+
+#endif
--- /dev/null
+/*
+ * IXP2000 MSF network device driver
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __IXPDEV_PRIV_H
+#define __IXPDEV_PRIV_H
+
+#define RX_BUF_DESC_BASE 0x00001000
+#define RX_BUF_COUNT ((3 * PAGE_SIZE) / (4 * sizeof(struct ixpdev_rx_desc)))
+#define TX_BUF_DESC_BASE 0x00002000
+#define TX_BUF_COUNT ((3 * PAGE_SIZE) / (4 * sizeof(struct ixpdev_tx_desc)))
+#define TX_BUF_COUNT_PER_CHAN (TX_BUF_COUNT / 4)
+
+#define RING_RX_PENDING ((u32 *)IXP2000_SCRATCH_RING_VIRT_BASE)
+#define RING_RX_DONE ((u32 *)(IXP2000_SCRATCH_RING_VIRT_BASE + 4))
+#define RING_TX_PENDING ((u32 *)(IXP2000_SCRATCH_RING_VIRT_BASE + 8))
+#define RING_TX_DONE ((u32 *)(IXP2000_SCRATCH_RING_VIRT_BASE + 12))
+
+#define SCRATCH_REG(x) ((u32 *)(IXP2000_GLOBAL_REG_VIRT_BASE | 0x0800 | (x)))
+#define RING_RX_PENDING_BASE SCRATCH_REG(0x00)
+#define RING_RX_PENDING_HEAD SCRATCH_REG(0x04)
+#define RING_RX_PENDING_TAIL SCRATCH_REG(0x08)
+#define RING_RX_DONE_BASE SCRATCH_REG(0x10)
+#define RING_RX_DONE_HEAD SCRATCH_REG(0x14)
+#define RING_RX_DONE_TAIL SCRATCH_REG(0x18)
+#define RING_TX_PENDING_BASE SCRATCH_REG(0x20)
+#define RING_TX_PENDING_HEAD SCRATCH_REG(0x24)
+#define RING_TX_PENDING_TAIL SCRATCH_REG(0x28)
+#define RING_TX_DONE_BASE SCRATCH_REG(0x30)
+#define RING_TX_DONE_HEAD SCRATCH_REG(0x34)
+#define RING_TX_DONE_TAIL SCRATCH_REG(0x38)
+
+struct ixpdev_rx_desc
+{
+ u32 buf_addr;
+ u32 buf_length;
+ u32 channel;
+ u32 pkt_length;
+};
+
+struct ixpdev_tx_desc
+{
+ u32 buf_addr;
+ u32 pkt_length;
+ u32 channel;
+ u32 unused;
+};
+
+
+#endif
--- /dev/null
+/*
+ * Helper functions for the PM3386s on the Radisys ENP2611
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <asm/io.h>
+
+/*
+ * Read from register 'reg' of PM3386 device 'pm'.
+ */
+static u16 pm3386_reg_read(int pm, int reg)
+{
+ void *_reg;
+ u16 value;
+
+ _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
+ if (pm == 1)
+ _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
+
+ value = *((volatile u16 *)(_reg + (reg << 1)));
+
+// printk(KERN_INFO "pm3386_reg_read(%d, %.3x) = %.8x\n", pm, reg, value);
+
+ return value;
+}
+
+/*
+ * Write to register 'reg' of PM3386 device 'pm', and perform
+ * a readback from the identification register.
+ */
+static void pm3386_reg_write(int pm, int reg, u16 value)
+{
+ void *_reg;
+ u16 dummy;
+
+// printk(KERN_INFO "pm3386_reg_write(%d, %.3x, %.8x)\n", pm, reg, value);
+
+ _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
+ if (pm == 1)
+ _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
+
+ *((volatile u16 *)(_reg + (reg << 1))) = value;
+
+ dummy = *((volatile u16 *)_reg);
+ __asm__ __volatile__("mov %0, %0" : "+r" (dummy));
+}
+
+/*
+ * Read from port 'port' register 'reg', where the registers
+ * for the different ports are 'spacing' registers apart.
+ */
+static u16 pm3386_port_reg_read(int port, int _reg, int spacing)
+{
+ int reg;
+
+ reg = _reg;
+ if (port & 1)
+ reg += spacing;
+
+ return pm3386_reg_read(port >> 1, reg);
+}
+
+/*
+ * Write to port 'port' register 'reg', where the registers
+ * for the different ports are 'spacing' registers apart.
+ */
+static void pm3386_port_reg_write(int port, int _reg, int spacing, u16 value)
+{
+ int reg;
+
+ reg = _reg;
+ if (port & 1)
+ reg += spacing;
+
+ pm3386_reg_write(port >> 1, reg, value);
+}
+
+
+void pm3386_reset(void)
+{
+ /* @@@ Implement me. */
+}
+
+static u16 swaph(u16 x)
+{
+ return ((x << 8) | (x >> 8)) & 0xffff;
+}
+
+void pm3386_init_port(int port)
+{
+ int pm = port >> 1;
+
+ /*
+ * Work around ENP2611 bootloader programming MAC address
+ * in reverse.
+ */
+ if (pm3386_port_reg_read(port, 0x30a, 0x100) == 0x0000 &&
+ (pm3386_port_reg_read(port, 0x309, 0x100) & 0xff00) == 0x5000) {
+ u16 temp[3];
+
+ temp[0] = pm3386_port_reg_read(port, 0x308, 0x100);
+ temp[1] = pm3386_port_reg_read(port, 0x309, 0x100);
+ temp[2] = pm3386_port_reg_read(port, 0x30a, 0x100);
+ pm3386_port_reg_write(port, 0x308, 0x100, swaph(temp[2]));
+ pm3386_port_reg_write(port, 0x309, 0x100, swaph(temp[1]));
+ pm3386_port_reg_write(port, 0x30a, 0x100, swaph(temp[0]));
+ }
+
+ /*
+ * Initialise narrowbanding mode. See application note 2010486
+ * for more information. (@@@ We also need to issue a reset
+ * when ROOL or DOOL are detected.)
+ */
+ pm3386_port_reg_write(port, 0x708, 0x10, 0xd055);
+ udelay(500);
+ pm3386_port_reg_write(port, 0x708, 0x10, 0x5055);
+
+ /*
+ * SPI-3 ingress block. Set 64 bytes SPI-3 burst size
+ * towards SPI-3 bridge.
+ */
+ pm3386_port_reg_write(port, 0x122, 0x20, 0x0002);
+
+ /*
+ * Enable ingress protocol checking, and soft reset the
+ * SPI-3 ingress block.
+ */
+ pm3386_reg_write(pm, 0x103, 0x0003);
+ while (!(pm3386_reg_read(pm, 0x103) & 0x80))
+ ;
+
+ /*
+ * SPI-3 egress block. Gather 12288 bytes of the current
+ * packet in the TX fifo before initiating transmit on the
+ * SERDES interface. (Prevents TX underflows.)
+ */
+ pm3386_port_reg_write(port, 0x221, 0x20, 0x0007);
+
+ /*
+ * Enforce odd parity from the SPI-3 bridge, and soft reset
+ * the SPI-3 egress block.
+ */
+ pm3386_reg_write(pm, 0x203, 0x000d & ~(4 << (port & 1)));
+ while ((pm3386_reg_read(pm, 0x203) & 0x000c) != 0x000c)
+ ;
+
+ /*
+ * EGMAC block. Set this channels to reject long preambles,
+ * not send or transmit PAUSE frames, enable preamble checking,
+ * disable frame length checking, enable FCS appending, enable
+ * TX frame padding.
+ */
+ pm3386_port_reg_write(port, 0x302, 0x100, 0x0113);
+
+ /*
+ * Soft reset the EGMAC block.
+ */
+ pm3386_port_reg_write(port, 0x301, 0x100, 0x8000);
+ udelay(10);
+ pm3386_port_reg_write(port, 0x301, 0x100, 0x0000);
+ udelay(10);
+
+ /*
+ * Auto-sense autonegotiation status.
+ */
+ pm3386_port_reg_write(port, 0x306, 0x100, 0x0100);
+
+ /*
+ * Allow reception of jumbo frames.
+ */
+ pm3386_port_reg_write(port, 0x310, 0x100, 9018);
+
+ /*
+ * Allow transmission of jumbo frames.
+ */
+ pm3386_port_reg_write(port, 0x336, 0x100, 9018);
+
+ /* @@@ Should set 0x337/0x437 (RX forwarding threshold.) */
+
+ /*
+ * Set autonegotiation parameters to 'no PAUSE, full duplex.'
+ */
+ pm3386_port_reg_write(port, 0x31c, 0x100, 0x0020);
+ udelay(10);
+
+ /*
+ * Enable and restart autonegotiation.
+ */
+ pm3386_port_reg_write(port, 0x318, 0x100, 0x0003);
+ udelay(1000);
+ pm3386_port_reg_write(port, 0x318, 0x100, 0x0002);
+ udelay(10);
+}
+
+void pm3386_get_mac(int port, u8 *mac)
+{
+ u16 temp;
+
+ temp = pm3386_port_reg_read(port, 0x308, 0x100);
+ mac[0] = temp & 0xff;
+ mac[1] = (temp >> 8) & 0xff;
+
+ temp = pm3386_port_reg_read(port, 0x309, 0x100);
+ mac[2] = temp & 0xff;
+ mac[3] = (temp >> 8) & 0xff;
+
+ temp = pm3386_port_reg_read(port, 0x30a, 0x100);
+ mac[4] = temp & 0xff;
+ mac[5] = (temp >> 8) & 0xff;
+}
+
+static u32 pm3386_get_stat(int port, u16 base)
+{
+ u32 value;
+
+ value = pm3386_port_reg_read(port, base, 0x100);
+ value |= pm3386_port_reg_read(port, base + 1, 0x100) << 16;
+
+ return value;
+}
+
+void pm3386_get_stats(int port, struct net_device_stats *stats)
+{
+ /*
+ * Snapshot statistics counters.
+ */
+ pm3386_port_reg_write(port, 0x500, 0x100, 0x0001);
+ while (pm3386_port_reg_read(port, 0x500, 0x100) & 0x0001)
+ ;
+
+ memset(stats, 0, sizeof(stats));
+
+ stats->rx_packets = pm3386_get_stat(port, 0x510);
+ stats->tx_packets = pm3386_get_stat(port, 0x590);
+ stats->rx_bytes = pm3386_get_stat(port, 0x514);
+ stats->tx_bytes = pm3386_get_stat(port, 0x594);
+ /* @@@ Add other stats. */
+}
+
+int pm3386_is_link_up(int port)
+{
+ u16 temp;
+
+ temp = pm3386_port_reg_read(port, 0x31a, 0x100);
+ temp = pm3386_port_reg_read(port, 0x31a, 0x100);
+
+ return !!(temp & 0x0002);
+}
+
+void pm3386_enable_rx(int port)
+{
+ u16 temp;
+
+ temp = pm3386_port_reg_read(port, 0x303, 0x100);
+ temp |= 0x1000;
+ pm3386_port_reg_write(port, 0x303, 0x100, temp);
+
+ udelay(10);
+}
+
+void pm3386_disable_rx(int port)
+{
+ u16 temp;
+
+ temp = pm3386_port_reg_read(port, 0x303, 0x100);
+ temp &= 0xefff;
+ pm3386_port_reg_write(port, 0x303, 0x100, temp);
+
+ udelay(10);
+}
+
+void pm3386_enable_tx(int port)
+{
+ u16 temp;
+
+ temp = pm3386_port_reg_read(port, 0x303, 0x100);
+ temp |= 0x4000;
+ pm3386_port_reg_write(port, 0x303, 0x100, temp);
+
+ udelay(10);
+}
+
+void pm3386_disable_tx(int port)
+{
+ u16 temp;
+
+ temp = pm3386_port_reg_read(port, 0x303, 0x100);
+ temp &= 0xbfff;
+ pm3386_port_reg_write(port, 0x303, 0x100, temp);
+
+ udelay(10);
+}
+
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Helper functions for the PM3386s on the Radisys ENP2611
+ * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
+ * Dedicated to Marija Kulikova.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PM3386_H
+#define __PM3386_H
+
+void pm3386_reset(void);
+void pm3386_init_port(int port);
+void pm3386_get_mac(int port, u8 *mac);
+void pm3386_get_stats(int port, struct net_device_stats *stats);
+int pm3386_is_link_up(int port);
+void pm3386_enable_rx(int port);
+void pm3386_disable_rx(int port);
+void pm3386_enable_tx(int port);
+void pm3386_disable_tx(int port);
+
+
+#endif
#include "s2io.h"
#include "s2io-regs.h"
-#define DRV_VERSION "Version 2.0.9.3"
+#define DRV_VERSION "Version 2.0.9.4"
/* S2io Driver name & version. */
static char s2io_driver_name[] = "Neterion";
config->tx_cfg[i].fifo_len - 1;
mac_control->fifos[i].fifo_no = i;
mac_control->fifos[i].nic = nic;
- mac_control->fifos[i].max_txds = MAX_SKB_FRAGS + 1;
+ mac_control->fifos[i].max_txds = MAX_SKB_FRAGS + 2;
for (j = 0; j < page_num; j++) {
int k = 0;
}
}
+ nic->ufo_in_band_v = kmalloc((sizeof(u64) * size), GFP_KERNEL);
+ if (!nic->ufo_in_band_v)
+ return -ENOMEM;
+
/* Allocation and initialization of RXDs in Rings */
size = 0;
for (i = 0; i < config->rx_ring_num; i++) {
mac_control->stats_mem,
mac_control->stats_mem_phy);
}
+ if (nic->ufo_in_band_v)
+ kfree(nic->ufo_in_band_v);
}
/**
return SUCCESS;
}
+/**
+ * s2io_txdl_getskb - Get the skb from txdl, unmap and return skb
+ */
+static struct sk_buff *s2io_txdl_getskb(fifo_info_t *fifo_data, TxD_t *txdlp, int get_off)
+{
+ nic_t *nic = fifo_data->nic;
+ struct sk_buff *skb;
+ TxD_t *txds;
+ u16 j, frg_cnt;
+
+ txds = txdlp;
+ if (txds->Host_Control == (u64) nic->ufo_in_band_v) {
+ pci_unmap_single(nic->pdev, (dma_addr_t)
+ txds->Buffer_Pointer, sizeof(u64),
+ PCI_DMA_TODEVICE);
+ txds++;
+ }
+
+ skb = (struct sk_buff *) ((unsigned long)
+ txds->Host_Control);
+ if (!skb) {
+ memset(txdlp, 0, (sizeof(TxD_t) * fifo_data->max_txds));
+ return NULL;
+ }
+ pci_unmap_single(nic->pdev, (dma_addr_t)
+ txds->Buffer_Pointer,
+ skb->len - skb->data_len,
+ PCI_DMA_TODEVICE);
+ frg_cnt = skb_shinfo(skb)->nr_frags;
+ if (frg_cnt) {
+ txds++;
+ for (j = 0; j < frg_cnt; j++, txds++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[j];
+ if (!txds->Buffer_Pointer)
+ break;
+ pci_unmap_page(nic->pdev, (dma_addr_t)
+ txds->Buffer_Pointer,
+ frag->size, PCI_DMA_TODEVICE);
+ }
+ }
+ txdlp->Host_Control = 0;
+ return(skb);
+}
/**
* free_tx_buffers - Free all queued Tx buffers
int i, j;
mac_info_t *mac_control;
struct config_param *config;
- int cnt = 0, frg_cnt;
+ int cnt = 0;
mac_control = &nic->mac_control;
config = &nic->config;
for (j = 0; j < config->tx_cfg[i].fifo_len - 1; j++) {
txdp = (TxD_t *) mac_control->fifos[i].list_info[j].
list_virt_addr;
- skb =
- (struct sk_buff *) ((unsigned long) txdp->
- Host_Control);
- if (skb == NULL) {
- memset(txdp, 0, sizeof(TxD_t) *
- config->max_txds);
- continue;
- }
- frg_cnt = skb_shinfo(skb)->nr_frags;
- pci_unmap_single(nic->pdev, (dma_addr_t)
- txdp->Buffer_Pointer,
- skb->len - skb->data_len,
- PCI_DMA_TODEVICE);
- if (frg_cnt) {
- TxD_t *temp;
- temp = txdp;
- txdp++;
- for (j = 0; j < frg_cnt; j++, txdp++) {
- skb_frag_t *frag =
- &skb_shinfo(skb)->frags[j];
- pci_unmap_page(nic->pdev,
- (dma_addr_t)
- txdp->
- Buffer_Pointer,
- frag->size,
- PCI_DMA_TODEVICE);
- }
- txdp = temp;
+ skb = s2io_txdl_getskb(&mac_control->fifos[i], txdp, j);
+ if (skb) {
+ dev_kfree_skb(skb);
+ cnt++;
}
- dev_kfree_skb(skb);
- memset(txdp, 0, sizeof(TxD_t) * config->max_txds);
- cnt++;
}
DBG_PRINT(INTR_DBG,
"%s:forcibly freeing %d skbs on FIFO%d\n",
tx_curr_get_info_t get_info, put_info;
struct sk_buff *skb;
TxD_t *txdlp;
- u16 j, frg_cnt;
get_info = fifo_data->tx_curr_get_info;
put_info = fifo_data->tx_curr_put_info;
}
}
- skb = (struct sk_buff *) ((unsigned long)
- txdlp->Host_Control);
+ skb = s2io_txdl_getskb(fifo_data, txdlp, get_info.offset);
if (skb == NULL) {
DBG_PRINT(ERR_DBG, "%s: Null skb ",
__FUNCTION__);
return;
}
- frg_cnt = skb_shinfo(skb)->nr_frags;
- nic->tx_pkt_count++;
-
- pci_unmap_single(nic->pdev, (dma_addr_t)
- txdlp->Buffer_Pointer,
- skb->len - skb->data_len,
- PCI_DMA_TODEVICE);
- if (frg_cnt) {
- TxD_t *temp;
- temp = txdlp;
- txdlp++;
- for (j = 0; j < frg_cnt; j++, txdlp++) {
- skb_frag_t *frag =
- &skb_shinfo(skb)->frags[j];
- if (!txdlp->Buffer_Pointer)
- break;
- pci_unmap_page(nic->pdev,
- (dma_addr_t)
- txdlp->
- Buffer_Pointer,
- frag->size,
- PCI_DMA_TODEVICE);
- }
- txdlp = temp;
- }
- memset(txdlp, 0,
- (sizeof(TxD_t) * fifo_data->max_txds));
-
/* Updating the statistics block */
nic->stats.tx_bytes += skb->len;
dev_kfree_skb_irq(skb);
return 0;
}
+ txdp->Control_1 = 0;
+ txdp->Control_2 = 0;
#ifdef NETIF_F_TSO
mss = skb_shinfo(skb)->tso_size;
if (mss) {
txdp->Control_1 |= TXD_TCP_LSO_MSS(mss);
}
#endif
-
- frg_cnt = skb_shinfo(skb)->nr_frags;
- frg_len = skb->len - skb->data_len;
-
- txdp->Buffer_Pointer = pci_map_single
- (sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE);
- txdp->Host_Control = (unsigned long) skb;
if (skb->ip_summed == CHECKSUM_HW) {
txdp->Control_2 |=
(TXD_TX_CKO_IPV4_EN | TXD_TX_CKO_TCP_EN |
TXD_TX_CKO_UDP_EN);
}
-
+ txdp->Control_1 |= TXD_GATHER_CODE_FIRST;
+ txdp->Control_1 |= TXD_LIST_OWN_XENA;
txdp->Control_2 |= config->tx_intr_type;
if (sp->vlgrp && vlan_tx_tag_present(skb)) {
txdp->Control_2 |= TXD_VLAN_TAG(vlan_tag);
}
- txdp->Control_1 |= (TXD_BUFFER0_SIZE(frg_len) |
- TXD_GATHER_CODE_FIRST);
- txdp->Control_1 |= TXD_LIST_OWN_XENA;
+ frg_len = skb->len - skb->data_len;
+ if (skb_shinfo(skb)->ufo_size) {
+ int ufo_size;
+
+ ufo_size = skb_shinfo(skb)->ufo_size;
+ ufo_size &= ~7;
+ txdp->Control_1 |= TXD_UFO_EN;
+ txdp->Control_1 |= TXD_UFO_MSS(ufo_size);
+ txdp->Control_1 |= TXD_BUFFER0_SIZE(8);
+#ifdef __BIG_ENDIAN
+ sp->ufo_in_band_v[put_off] =
+ (u64)skb_shinfo(skb)->ip6_frag_id;
+#else
+ sp->ufo_in_band_v[put_off] =
+ (u64)skb_shinfo(skb)->ip6_frag_id << 32;
+#endif
+ txdp->Host_Control = (unsigned long)sp->ufo_in_band_v;
+ txdp->Buffer_Pointer = pci_map_single(sp->pdev,
+ sp->ufo_in_band_v,
+ sizeof(u64), PCI_DMA_TODEVICE);
+ txdp++;
+ txdp->Control_1 = 0;
+ txdp->Control_2 = 0;
+ }
+
+ txdp->Buffer_Pointer = pci_map_single
+ (sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE);
+ txdp->Host_Control = (unsigned long) skb;
+ txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len);
+ if (skb_shinfo(skb)->ufo_size)
+ txdp->Control_1 |= TXD_UFO_EN;
+
+ frg_cnt = skb_shinfo(skb)->nr_frags;
/* For fragmented SKB. */
for (i = 0; i < frg_cnt; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
(sp->pdev, frag->page, frag->page_offset,
frag->size, PCI_DMA_TODEVICE);
txdp->Control_1 |= TXD_BUFFER0_SIZE(frag->size);
+ if (skb_shinfo(skb)->ufo_size)
+ txdp->Control_1 |= TXD_UFO_EN;
}
txdp->Control_1 |= TXD_GATHER_CODE_LAST;
+ if (skb_shinfo(skb)->ufo_size)
+ frg_cnt++; /* as Txd0 was used for inband header */
+
tx_fifo = mac_control->tx_FIFO_start[queue];
val64 = mac_control->fifos[queue].list_info[put_off].list_phy_addr;
writeq(val64, &tx_fifo->TxDL_Pointer);
if (mss)
val64 |= TX_FIFO_SPECIAL_FUNC;
#endif
+ if (skb_shinfo(skb)->ufo_size)
+ val64 |= TX_FIFO_SPECIAL_FUNC;
writeq(val64, &tx_fifo->List_Control);
mmiowb();
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
#endif
+ .get_ufo = ethtool_op_get_ufo,
+ .set_ufo = ethtool_op_set_ufo,
.self_test_count = s2io_ethtool_self_test_count,
.self_test = s2io_ethtool_test,
.get_strings = s2io_ethtool_get_strings,
break;
}
}
- config->max_txds = MAX_SKB_FRAGS + 1;
+ /* + 2 because one Txd for skb->data and one Txd for UFO */
+ config->max_txds = MAX_SKB_FRAGS + 2;
/* Rx side parameters. */
if (rx_ring_sz[0] == 0)
#ifdef NETIF_F_TSO
dev->features |= NETIF_F_TSO;
#endif
+ if (sp->device_type & XFRAME_II_DEVICE) {
+ dev->features |= NETIF_F_UFO;
+ dev->features |= NETIF_F_HW_CSUM;
+ }
dev->tx_timeout = &s2io_tx_watchdog;
dev->watchdog_timeo = WATCH_DOG_TIMEOUT;
#define TXD_GATHER_CODE_LAST BIT(23)
#define TXD_TCP_LSO_EN BIT(30)
#define TXD_UDP_COF_EN BIT(31)
+#define TXD_UFO_EN BIT(31) | BIT(30)
#define TXD_TCP_LSO_MSS(val) vBIT(val,34,14)
+#define TXD_UFO_MSS(val) vBIT(val,34,14)
#define TXD_BUFFER0_SIZE(val) vBIT(val,48,16)
u64 Control_2;
spinlock_t rx_lock;
atomic_t isr_cnt;
+ u64 *ufo_in_band_v;
};
#define RESET_ERROR 1;
/* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
Copyright 1999 Silicon Integrated System Corporation
- Revision: 1.08.08 Jan. 22 2005
+ Revision: 1.08.09 Sep. 19 2005
Modified from the driver which is originally written by Donald Becker.
SiS 7014 Single Chip 100BASE-TX/10BASE-T Physical Layer Solution,
preliminary Rev. 1.0 Jan. 18, 1998
+ Rev 1.08.09 Sep. 19 2005 Daniele Venzano add Wake on LAN support
Rev 1.08.08 Jan. 22 2005 Daniele Venzano use netif_msg for debugging messages
Rev 1.08.07 Nov. 2 2003 Daniele Venzano <webvenza@libero.it> add suspend/resume support
Rev 1.08.06 Sep. 24 2002 Mufasa Yang bug fix for Tx timeout & add SiS963 support
#include "sis900.h"
#define SIS900_MODULE_NAME "sis900"
-#define SIS900_DRV_VERSION "v1.08.08 Jan. 22 2005"
+#define SIS900_DRV_VERSION "v1.08.09 Sep. 19 2005"
static char version[] __devinitdata =
KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n";
printk("%2.2x:", (u8)net_dev->dev_addr[i]);
printk("%2.2x.\n", net_dev->dev_addr[i]);
+ /* Detect Wake on Lan support */
+ ret = inl(CFGPMC & PMESP);
+ if (netif_msg_probe(sis_priv) && (ret & PME_D3C) == 0)
+ printk(KERN_INFO "%s: Wake on LAN only available from suspend to RAM.", net_dev->name);
+
return 0;
err_unmap_rx:
return mii_nway_restart(&sis_priv->mii_info);
}
+/**
+ * sis900_set_wol - Set up Wake on Lan registers
+ * @net_dev: the net device to probe
+ * @wol: container for info passed to the driver
+ *
+ * Process ethtool command "wol" to setup wake on lan features.
+ * SiS900 supports sending WoL events if a correct packet is received,
+ * but there is no simple way to filter them to only a subset (broadcast,
+ * multicast, unicast or arp).
+ */
+
+static int sis900_set_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol)
+{
+ struct sis900_private *sis_priv = net_dev->priv;
+ long pmctrl_addr = net_dev->base_addr + pmctrl;
+ u32 cfgpmcsr = 0, pmctrl_bits = 0;
+
+ if (wol->wolopts == 0) {
+ pci_read_config_dword(sis_priv->pci_dev, CFGPMCSR, &cfgpmcsr);
+ cfgpmcsr |= ~PME_EN;
+ pci_write_config_dword(sis_priv->pci_dev, CFGPMCSR, cfgpmcsr);
+ outl(pmctrl_bits, pmctrl_addr);
+ if (netif_msg_wol(sis_priv))
+ printk(KERN_DEBUG "%s: Wake on LAN disabled\n", net_dev->name);
+ return 0;
+ }
+
+ if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_UCAST | WAKE_MCAST
+ | WAKE_BCAST | WAKE_ARP))
+ return -EINVAL;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ pmctrl_bits |= MAGICPKT;
+ if (wol->wolopts & WAKE_PHY)
+ pmctrl_bits |= LINKON;
+
+ outl(pmctrl_bits, pmctrl_addr);
+
+ pci_read_config_dword(sis_priv->pci_dev, CFGPMCSR, &cfgpmcsr);
+ cfgpmcsr |= PME_EN;
+ pci_write_config_dword(sis_priv->pci_dev, CFGPMCSR, cfgpmcsr);
+ if (netif_msg_wol(sis_priv))
+ printk(KERN_DEBUG "%s: Wake on LAN enabled\n", net_dev->name);
+
+ return 0;
+}
+
+static void sis900_get_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol)
+{
+ long pmctrl_addr = net_dev->base_addr + pmctrl;
+ u32 pmctrl_bits;
+
+ pmctrl_bits = inl(pmctrl_addr);
+ if (pmctrl_bits & MAGICPKT)
+ wol->wolopts |= WAKE_MAGIC;
+ if (pmctrl_bits & LINKON)
+ wol->wolopts |= WAKE_PHY;
+
+ wol->supported = (WAKE_PHY | WAKE_MAGIC);
+}
+
static struct ethtool_ops sis900_ethtool_ops = {
.get_drvinfo = sis900_get_drvinfo,
.get_msglevel = sis900_get_msglevel,
.get_settings = sis900_get_settings,
.set_settings = sis900_set_settings,
.nway_reset = sis900_nway_reset,
+ .get_wol = sis900_get_wol,
+ .set_wol = sis900_set_wol
};
/**
rxcfg=0x34, //Receive Configuration Register
flctrl=0x38, //Flow Control Register
rxlen=0x3c, //Receive Packet Length Register
+ cfgpmcsr=0x44, //Configuration Power Management Control/Status Register
rfcr=0x48, //Receive Filter Control Register
rfdr=0x4C, //Receive Filter Data Register
pmctrl=0xB0, //Power Management Control Register
EEREQ = 0x00000400, EEDONE = 0x00000200, EEGNT = 0x00000100
};
+/* PCI Registers */
+enum sis900_pci_registers {
+ CFGPMC = 0x40,
+ CFGPMCSR = 0x44
+};
+
+/* Power management capabilities bits */
+enum sis900_cfgpmc_register_bits {
+ PMVER = 0x00070000,
+ DSI = 0x00100000,
+ PMESP = 0xf8000000
+};
+
+enum sis900_pmesp_bits {
+ PME_D0 = 0x1,
+ PME_D1 = 0x2,
+ PME_D2 = 0x4,
+ PME_D3H = 0x8,
+ PME_D3C = 0x10
+};
+
+/* Power management control/status bits */
+enum sis900_cfgpmcsr_register_bits {
+ PMESTS = 0x00004000,
+ PME_EN = 0x00000100, // Power management enable
+ PWR_STA = 0x00000003 // Current power state
+};
+
+/* Wake-on-LAN support. */
+enum sis900_power_management_control_register_bits {
+ LINKLOSS = 0x00000001,
+ LINKON = 0x00000002,
+ MAGICPKT = 0x00000400,
+ ALGORITHM = 0x00000800,
+ FRM1EN = 0x00100000,
+ FRM2EN = 0x00200000,
+ FRM3EN = 0x00400000,
+ FRM1ACS = 0x01000000,
+ FRM2ACS = 0x02000000,
+ FRM3ACS = 0x04000000,
+ WAKEALL = 0x40000000,
+ GATECLK = 0x80000000
+};
+
/* Management Data I/O (mdio) frame */
#define MIIread 0x6000
#define MIIwrite 0x5002
extern int SkPciReadCfgDWord(SK_AC*, int, SK_U32*);
extern int SkPciReadCfgWord(SK_AC*, int, SK_U16*);
extern int SkPciReadCfgByte(SK_AC*, int, SK_U8*);
-extern int SkPciWriteCfgDWord(SK_AC*, int, SK_U32);
extern int SkPciWriteCfgWord(SK_AC*, int, SK_U16);
extern int SkPciWriteCfgByte(SK_AC*, int, SK_U8);
extern int SkDrvEvent(SK_AC*, SK_IOC IoC, SK_U32, SK_EVPARA);
#ifndef VPD_DO_IO
#define VPD_OUT8(pAC,IoC,Addr,Val) (void)SkPciWriteCfgByte(pAC,Addr,Val)
#define VPD_OUT16(pAC,IoC,Addr,Val) (void)SkPciWriteCfgWord(pAC,Addr,Val)
-#define VPD_OUT32(pAC,IoC,Addr,Val) (void)SkPciWriteCfgDWord(pAC,Addr,Val)
#define VPD_IN8(pAC,IoC,Addr,pVal) (void)SkPciReadCfgByte(pAC,Addr,pVal)
#define VPD_IN16(pAC,IoC,Addr,pVal) (void)SkPciReadCfgWord(pAC,Addr,pVal)
#define VPD_IN32(pAC,IoC,Addr,pVal) (void)SkPciReadCfgDWord(pAC,Addr,pVal)
#else /* VPD_DO_IO */
#define VPD_OUT8(pAC,IoC,Addr,Val) SK_OUT8(IoC,PCI_C(Addr),Val)
#define VPD_OUT16(pAC,IoC,Addr,Val) SK_OUT16(IoC,PCI_C(Addr),Val)
-#define VPD_OUT32(pAC,IoC,Addr,Val) SK_OUT32(IoC,PCI_C(Addr),Val)
#define VPD_IN8(pAC,IoC,Addr,pVal) SK_IN8(IoC,PCI_C(Addr),pVal)
#define VPD_IN16(pAC,IoC,Addr,pVal) SK_IN16(IoC,PCI_C(Addr),pVal)
#define VPD_IN32(pAC,IoC,Addr,pVal) SK_IN32(IoC,PCI_C(Addr),pVal)
else \
SK_OUT16(pAC,PCI_C(Addr),Val); \
}
-#define VPD_OUT32(pAC,Ioc,Addr,Val) { \
- if ((pAC)->DgT.DgUseCfgCycle) \
- SkPciWriteCfgDWord(pAC,Addr,Val); \
- else \
- SK_OUT32(pAC,PCI_C(Addr),Val); \
- }
#define VPD_IN8(pAC,Ioc,Addr,pVal) { \
if ((pAC)->DgT.DgUseCfgCycle) \
SkPciReadCfgByte(pAC,Addr,pVal); \
static uintptr_t TxQueueAddr[SK_MAX_MACS][2] = {{0x680, 0x600},{0x780, 0x700}};
static uintptr_t RxQueueAddr[SK_MAX_MACS] = {0x400, 0x480};
+/*****************************************************************************
+ *
+ * SkPciWriteCfgDWord - write a 32 bit value to pci config space
+ *
+ * Description:
+ * This routine writes a 32 bit value to the pci configuration
+ * space.
+ *
+ * Returns:
+ * 0 - indicate everything worked ok.
+ * != 0 - error indication
+ */
+static inline int SkPciWriteCfgDWord(
+SK_AC *pAC, /* Adapter Control structure pointer */
+int PciAddr, /* PCI register address */
+SK_U32 Val) /* pointer to store the read value */
+{
+ pci_write_config_dword(pAC->PciDev, PciAddr, Val);
+ return(0);
+} /* SkPciWriteCfgDWord */
+
/*****************************************************************************
*
* SkGeInitPCI - Init the PCI resources
} /* SkPciReadCfgByte */
-/*****************************************************************************
- *
- * SkPciWriteCfgDWord - write a 32 bit value to pci config space
- *
- * Description:
- * This routine writes a 32 bit value to the pci configuration
- * space.
- *
- * Returns:
- * 0 - indicate everything worked ok.
- * != 0 - error indication
- */
-int SkPciWriteCfgDWord(
-SK_AC *pAC, /* Adapter Control structure pointer */
-int PciAddr, /* PCI register address */
-SK_U32 Val) /* pointer to store the read value */
-{
- pci_write_config_dword(pAC->PciDev, PciAddr, Val);
- return(0);
-} /* SkPciWriteCfgDWord */
-
-
/*****************************************************************************
*
* SkPciWriteCfgWord - write a 16 bit value to pci config space
--- /dev/null
+/*
+ * New driver for Marvell Yukon 2 chipset.
+ * Based on earlier sk98lin, and skge driver.
+ *
+ * This driver intentionally does not support all the features
+ * of the original driver such as link fail-over and link management because
+ * those should be done at higher levels.
+ *
+ * Copyright (C) 2005 Stephen Hemminger <shemminger@osdl.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * TODO
+ * - coalescing setting?
+ *
+ * TOTEST
+ * - speed setting
+ * - suspend/resume
+ */
+
+#include <linux/config.h>
+#include <linux/crc32.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/if_vlan.h>
+
+#include <asm/irq.h>
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define SKY2_VLAN_TAG_USED 1
+#endif
+
+#include "sky2.h"
+
+#define DRV_NAME "sky2"
+#define DRV_VERSION "0.7"
+#define PFX DRV_NAME " "
+
+/*
+ * The Yukon II chipset takes 64 bit command blocks (called list elements)
+ * that are organized into three (receive, transmit, status) different rings
+ * similar to Tigon3. A transmit can require several elements;
+ * a receive requires one (or two if using 64 bit dma).
+ */
+
+#ifdef CONFIG_SKY2_EC_A1
+#define is_ec_a1(hw) \
+ ((hw)->chip_id == CHIP_ID_YUKON_EC && \
+ (hw)->chip_rev == CHIP_REV_YU_EC_A1)
+#else
+#define is_ec_a1(hw) 0
+#endif
+
+#define RX_LE_SIZE 256
+#define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le))
+#define RX_MAX_PENDING (RX_LE_SIZE/2 - 2)
+#define RX_DEF_PENDING 128
+#define RX_COPY_THRESHOLD 256
+
+#define TX_RING_SIZE 512
+#define TX_DEF_PENDING (TX_RING_SIZE - 1)
+#define TX_MIN_PENDING 64
+#define MAX_SKB_TX_LE (4 + 2*MAX_SKB_FRAGS)
+
+#define STATUS_RING_SIZE 2048 /* 2 ports * (TX + 2*RX) */
+#define STATUS_LE_BYTES (STATUS_RING_SIZE*sizeof(struct sky2_status_le))
+#define ETH_JUMBO_MTU 9000
+#define TX_WATCHDOG (5 * HZ)
+#define NAPI_WEIGHT 64
+#define PHY_RETRIES 1000
+
+static const u32 default_msg =
+ NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK
+ | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR
+ | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_INTR;
+
+static int debug = -1; /* defaults above */
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+static const struct pci_device_id sky2_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) },
+ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) },
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b00) },
+ { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4340) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4341) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4342) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4343) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4344) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4345) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4346) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4347) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4350) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4351) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4360) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, sky2_id_table);
+
+/* Avoid conditionals by using array */
+static const unsigned txqaddr[] = { Q_XA1, Q_XA2 };
+static const unsigned rxqaddr[] = { Q_R1, Q_R2 };
+
+static const char *yukon_name[] = {
+ [CHIP_ID_YUKON_LITE - CHIP_ID_YUKON] = "Lite", /* 0xb0 */
+ [CHIP_ID_YUKON_LP - CHIP_ID_YUKON] = "LP", /* 0xb2 */
+ [CHIP_ID_YUKON_XL - CHIP_ID_YUKON] = "XL", /* 0xb3 */
+
+ [CHIP_ID_YUKON_EC - CHIP_ID_YUKON] = "EC", /* 0xb6 */
+ [CHIP_ID_YUKON_FE - CHIP_ID_YUKON] = "FE", /* 0xb7 */
+};
+
+
+/* Access to external PHY */
+static void gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val)
+{
+ int i;
+
+ gma_write16(hw, port, GM_SMI_DATA, val);
+ gma_write16(hw, port, GM_SMI_CTRL,
+ GM_SMI_CT_PHY_AD(PHY_ADDR_MARV) | GM_SMI_CT_REG_AD(reg));
+
+ for (i = 0; i < PHY_RETRIES; i++) {
+ if (!(gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY))
+ return;
+ udelay(1);
+ }
+ printk(KERN_WARNING PFX "%s: phy write timeout\n", hw->dev[port]->name);
+}
+
+static u16 gm_phy_read(struct sky2_hw *hw, unsigned port, u16 reg)
+{
+ int i;
+
+ gma_write16(hw, port, GM_SMI_CTRL, GM_SMI_CT_PHY_AD(PHY_ADDR_MARV)
+ | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD);
+
+ for (i = 0; i < PHY_RETRIES; i++) {
+ if (gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL)
+ goto ready;
+ udelay(1);
+ }
+
+ printk(KERN_WARNING PFX "%s: phy read timeout\n", hw->dev[port]->name);
+ready:
+ return gma_read16(hw, port, GM_SMI_DATA);
+}
+
+static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state)
+{
+ u16 power_control;
+ u32 reg1;
+ int vaux;
+ int ret = 0;
+
+ pr_debug("sky2_set_power_state %d\n", state);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+
+ pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_PMC, &power_control);
+ vaux = (sky2_read8(hw, B0_CTST) & Y2_VAUX_AVAIL) &&
+ (power_control & PCI_PM_CAP_PME_D3cold);
+
+ pci_read_config_word(hw->pdev, hw->pm_cap + PCI_PM_CTRL, &power_control);
+
+ power_control |= PCI_PM_CTRL_PME_STATUS;
+ power_control &= ~(PCI_PM_CTRL_STATE_MASK);
+
+ switch (state) {
+ case PCI_D0:
+ /* switch power to VCC (WA for VAUX problem) */
+ sky2_write8(hw, B0_POWER_CTRL,
+ PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON);
+
+ /* disable Core Clock Division, */
+ sky2_write32(hw, B2_Y2_CLK_CTRL, Y2_CLK_DIV_DIS);
+
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+ /* enable bits are inverted */
+ sky2_write8(hw, B2_Y2_CLK_GATE,
+ Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
+ Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
+ Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS);
+ else
+ sky2_write8(hw, B2_Y2_CLK_GATE, 0);
+
+ /* Turn off phy power saving */
+ pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1);
+ reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
+
+ /* looks like this XL is back asswards .. */
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) {
+ reg1 |= PCI_Y2_PHY1_COMA;
+ if (hw->ports > 1)
+ reg1 |= PCI_Y2_PHY2_COMA;
+ }
+ pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1);
+ break;
+
+ case PCI_D3hot:
+ case PCI_D3cold:
+ /* Turn on phy power saving */
+ pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1);
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+ reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
+ else
+ reg1 |= (PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD);
+ pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1);
+
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1)
+ sky2_write8(hw, B2_Y2_CLK_GATE, 0);
+ else
+ /* enable bits are inverted */
+ sky2_write8(hw, B2_Y2_CLK_GATE,
+ Y2_PCI_CLK_LNK1_DIS | Y2_COR_CLK_LNK1_DIS |
+ Y2_CLK_GAT_LNK1_DIS | Y2_PCI_CLK_LNK2_DIS |
+ Y2_COR_CLK_LNK2_DIS | Y2_CLK_GAT_LNK2_DIS);
+
+ /* switch power to VAUX */
+ if (vaux && state != PCI_D3cold)
+ sky2_write8(hw, B0_POWER_CTRL,
+ (PC_VAUX_ENA | PC_VCC_ENA |
+ PC_VAUX_ON | PC_VCC_OFF));
+ break;
+ default:
+ printk(KERN_ERR PFX "Unknown power state %d\n", state);
+ ret = -1;
+ }
+
+ pci_write_config_byte(hw->pdev, hw->pm_cap + PCI_PM_CTRL, power_control);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+ return ret;
+}
+
+static void sky2_phy_reset(struct sky2_hw *hw, unsigned port)
+{
+ u16 reg;
+
+ /* disable all GMAC IRQ's */
+ sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), 0);
+ /* disable PHY IRQs */
+ gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
+
+ gma_write16(hw, port, GM_MC_ADDR_H1, 0); /* clear MC hash */
+ gma_write16(hw, port, GM_MC_ADDR_H2, 0);
+ gma_write16(hw, port, GM_MC_ADDR_H3, 0);
+ gma_write16(hw, port, GM_MC_ADDR_H4, 0);
+
+ reg = gma_read16(hw, port, GM_RX_CTRL);
+ reg |= GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA;
+ gma_write16(hw, port, GM_RX_CTRL, reg);
+}
+
+static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
+{
+ struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
+ u16 ctrl, ct1000, adv, pg, ledctrl, ledover;
+
+ if (sky2->autoneg == AUTONEG_ENABLE && hw->chip_id != CHIP_ID_YUKON_XL) {
+ u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL);
+
+ ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK |
+ PHY_M_EC_MAC_S_MSK);
+ ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ);
+
+ if (hw->chip_id == CHIP_ID_YUKON_EC)
+ ectrl |= PHY_M_EC_DSC_2(2) | PHY_M_EC_DOWN_S_ENA;
+ else
+ ectrl |= PHY_M_EC_M_DSC(2) | PHY_M_EC_S_DSC(3);
+
+ gm_phy_write(hw, port, PHY_MARV_EXT_CTRL, ectrl);
+ }
+
+ ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+ if (hw->copper) {
+ if (hw->chip_id == CHIP_ID_YUKON_FE) {
+ /* enable automatic crossover */
+ ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO) >> 1;
+ } else {
+ /* disable energy detect */
+ ctrl &= ~PHY_M_PC_EN_DET_MSK;
+
+ /* enable automatic crossover */
+ ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO);
+
+ if (sky2->autoneg == AUTONEG_ENABLE &&
+ hw->chip_id == CHIP_ID_YUKON_XL) {
+ ctrl &= ~PHY_M_PC_DSC_MSK;
+ ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA;
+ }
+ }
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+ } else {
+ /* workaround for deviation #4.88 (CRC errors) */
+ /* disable Automatic Crossover */
+
+ ctrl &= ~PHY_M_PC_MDIX_MSK;
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+ if (hw->chip_id == CHIP_ID_YUKON_XL) {
+ /* Fiber: select 1000BASE-X only mode MAC Specific Ctrl Reg. */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 2);
+ ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+ ctrl &= ~PHY_M_MAC_MD_MSK;
+ ctrl |= PHY_M_MAC_MODE_SEL(PHY_M_MAC_MD_1000BX);
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+ /* select page 1 to access Fiber registers */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 1);
+ }
+ }
+
+ ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+ if (sky2->autoneg == AUTONEG_DISABLE)
+ ctrl &= ~PHY_CT_ANE;
+ else
+ ctrl |= PHY_CT_ANE;
+
+ ctrl |= PHY_CT_RESET;
+ gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+ ctrl = 0;
+ ct1000 = 0;
+ adv = PHY_AN_CSMA;
+
+ if (sky2->autoneg == AUTONEG_ENABLE) {
+ if (hw->copper) {
+ if (sky2->advertising & ADVERTISED_1000baseT_Full)
+ ct1000 |= PHY_M_1000C_AFD;
+ if (sky2->advertising & ADVERTISED_1000baseT_Half)
+ ct1000 |= PHY_M_1000C_AHD;
+ if (sky2->advertising & ADVERTISED_100baseT_Full)
+ adv |= PHY_M_AN_100_FD;
+ if (sky2->advertising & ADVERTISED_100baseT_Half)
+ adv |= PHY_M_AN_100_HD;
+ if (sky2->advertising & ADVERTISED_10baseT_Full)
+ adv |= PHY_M_AN_10_FD;
+ if (sky2->advertising & ADVERTISED_10baseT_Half)
+ adv |= PHY_M_AN_10_HD;
+ } else /* special defines for FIBER (88E1011S only) */
+ adv |= PHY_M_AN_1000X_AHD | PHY_M_AN_1000X_AFD;
+
+ /* Set Flow-control capabilities */
+ if (sky2->tx_pause && sky2->rx_pause)
+ adv |= PHY_AN_PAUSE_CAP; /* symmetric */
+ else if (sky2->rx_pause && !sky2->tx_pause)
+ adv |= PHY_AN_PAUSE_ASYM | PHY_AN_PAUSE_CAP;
+ else if (!sky2->rx_pause && sky2->tx_pause)
+ adv |= PHY_AN_PAUSE_ASYM; /* local */
+
+ /* Restart Auto-negotiation */
+ ctrl |= PHY_CT_ANE | PHY_CT_RE_CFG;
+ } else {
+ /* forced speed/duplex settings */
+ ct1000 = PHY_M_1000C_MSE;
+
+ if (sky2->duplex == DUPLEX_FULL)
+ ctrl |= PHY_CT_DUP_MD;
+
+ switch (sky2->speed) {
+ case SPEED_1000:
+ ctrl |= PHY_CT_SP1000;
+ break;
+ case SPEED_100:
+ ctrl |= PHY_CT_SP100;
+ break;
+ }
+
+ ctrl |= PHY_CT_RESET;
+ }
+
+ if (hw->chip_id != CHIP_ID_YUKON_FE)
+ gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, ct1000);
+
+ gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, adv);
+ gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+ /* Setup Phy LED's */
+ ledctrl = PHY_M_LED_PULS_DUR(PULS_170MS);
+ ledover = 0;
+
+ switch (hw->chip_id) {
+ case CHIP_ID_YUKON_FE:
+ /* on 88E3082 these bits are at 11..9 (shifted left) */
+ ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) << 1;
+
+ ctrl = gm_phy_read(hw, port, PHY_MARV_FE_LED_PAR);
+
+ /* delete ACT LED control bits */
+ ctrl &= ~PHY_M_FELP_LED1_MSK;
+ /* change ACT LED control to blink mode */
+ ctrl |= PHY_M_FELP_LED1_CTRL(LED_PAR_CTRL_ACT_BL);
+ gm_phy_write(hw, port, PHY_MARV_FE_LED_PAR, ctrl);
+ break;
+
+ case CHIP_ID_YUKON_XL:
+ pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+
+ /* select page 3 to access LED control register */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+
+ /* set LED Function Control register */
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */
+ PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */
+ PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */
+ PHY_M_LEDC_STA0_CTRL(7))); /* 1000 Mbps */
+
+ /* set Polarity Control register */
+ gm_phy_write(hw, port, PHY_MARV_PHY_STAT,
+ (PHY_M_POLC_LS1_P_MIX(4) |
+ PHY_M_POLC_IS0_P_MIX(4) |
+ PHY_M_POLC_LOS_CTRL(2) |
+ PHY_M_POLC_INIT_CTRL(2) |
+ PHY_M_POLC_STA1_CTRL(2) |
+ PHY_M_POLC_STA0_CTRL(2)));
+
+ /* restore page register */
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+ break;
+
+ default:
+ /* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */
+ ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) | PHY_M_LEDC_TX_CTRL;
+ /* turn off the Rx LED (LED_RX) */
+ ledover |= PHY_M_LED_MO_RX(MO_LED_OFF);
+ }
+
+ gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
+
+ if (sky2->autoneg == AUTONEG_DISABLE || sky2->speed == SPEED_100) {
+ /* turn on 100 Mbps LED (LED_LINK100) */
+ ledover |= PHY_M_LED_MO_100(MO_LED_ON);
+ }
+
+ if (ledover)
+ gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
+
+ /* Enable phy interrupt on auto-negotiation complete (or link up) */
+ if (sky2->autoneg == AUTONEG_ENABLE)
+ gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL);
+ else
+ gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+}
+
+static void sky2_mac_init(struct sky2_hw *hw, unsigned port)
+{
+ struct sky2_port *sky2 = netdev_priv(hw->dev[port]);
+ u16 reg;
+ int i;
+ const u8 *addr = hw->dev[port]->dev_addr;
+
+ sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+ sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR);
+
+ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR);
+
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0 && port == 1) {
+ /* WA DEV_472 -- looks like crossed wires on port 2 */
+ /* clear GMAC 1 Control reset */
+ sky2_write8(hw, SK_REG(0, GMAC_CTRL), GMC_RST_CLR);
+ do {
+ sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_SET);
+ sky2_write8(hw, SK_REG(1, GMAC_CTRL), GMC_RST_CLR);
+ } while (gm_phy_read(hw, 1, PHY_MARV_ID0) != PHY_MARV_ID0_VAL ||
+ gm_phy_read(hw, 1, PHY_MARV_ID1) != PHY_MARV_ID1_Y2 ||
+ gm_phy_read(hw, 1, PHY_MARV_INT_MASK) != 0);
+ }
+
+ if (sky2->autoneg == AUTONEG_DISABLE) {
+ reg = gma_read16(hw, port, GM_GP_CTRL);
+ reg |= GM_GPCR_AU_ALL_DIS;
+ gma_write16(hw, port, GM_GP_CTRL, reg);
+ gma_read16(hw, port, GM_GP_CTRL);
+
+ switch (sky2->speed) {
+ case SPEED_1000:
+ reg |= GM_GPCR_SPEED_1000;
+ /* fallthru */
+ case SPEED_100:
+ reg |= GM_GPCR_SPEED_100;
+ }
+
+ if (sky2->duplex == DUPLEX_FULL)
+ reg |= GM_GPCR_DUP_FULL;
+ } else
+ reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL;
+
+ if (!sky2->tx_pause && !sky2->rx_pause) {
+ sky2_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+ reg |=
+ GM_GPCR_FC_TX_DIS | GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS;
+ } else if (sky2->tx_pause && !sky2->rx_pause) {
+ /* disable Rx flow-control */
+ reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS;
+ }
+
+ gma_write16(hw, port, GM_GP_CTRL, reg);
+
+ sky2_read16(hw, SK_REG(port, GMAC_IRQ_SRC));
+
+ spin_lock_bh(&hw->phy_lock);
+ sky2_phy_init(hw, port);
+ spin_unlock_bh(&hw->phy_lock);
+
+ /* MIB clear */
+ reg = gma_read16(hw, port, GM_PHY_ADDR);
+ gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR);
+
+ for (i = 0; i < GM_MIB_CNT_SIZE; i++)
+ gma_read16(hw, port, GM_MIB_CNT_BASE + 8 * i);
+ gma_write16(hw, port, GM_PHY_ADDR, reg);
+
+ /* transmit control */
+ gma_write16(hw, port, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF));
+
+ /* receive control reg: unicast + multicast + no FCS */
+ gma_write16(hw, port, GM_RX_CTRL,
+ GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA);
+
+ /* transmit flow control */
+ gma_write16(hw, port, GM_TX_FLOW_CTRL, 0xffff);
+
+ /* transmit parameter */
+ gma_write16(hw, port, GM_TX_PARAM,
+ TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) |
+ TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) |
+ TX_IPG_JAM_DATA(TX_IPG_JAM_DEF) |
+ TX_BACK_OFF_LIM(TX_BOF_LIM_DEF));
+
+ /* serial mode register */
+ reg = DATA_BLIND_VAL(DATA_BLIND_DEF) |
+ GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+
+ if (hw->dev[port]->mtu > ETH_DATA_LEN)
+ reg |= GM_SMOD_JUMBO_ENA;
+
+ gma_write16(hw, port, GM_SERIAL_MODE, reg);
+
+ /* virtual address for data */
+ gma_set_addr(hw, port, GM_SRC_ADDR_2L, addr);
+
+ /* physical address: used for pause frames */
+ gma_set_addr(hw, port, GM_SRC_ADDR_1L, addr);
+
+ /* ignore counter overflows */
+ gma_write16(hw, port, GM_TX_IRQ_MSK, 0);
+ gma_write16(hw, port, GM_RX_IRQ_MSK, 0);
+ gma_write16(hw, port, GM_TR_IRQ_MSK, 0);
+
+ /* Configure Rx MAC FIFO */
+ sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR);
+ sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T),
+ GMF_RX_CTRL_DEF);
+
+ /* Flush Rx MAC FIFO on any flow control or error */
+ reg = GMR_FS_ANY_ERR;
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev <= 1)
+ reg = 0; /* WA dev #4.115 */
+
+ sky2_write16(hw, SK_REG(port, RX_GMF_FL_MSK), reg);
+ /* Set threshold to 0xa (64 bytes)
+ * ASF disabled so no need to do WA dev #4.30
+ */
+ sky2_write16(hw, SK_REG(port, RX_GMF_FL_THR), RX_GMF_FL_THR_DEF);
+
+ /* Configure Tx MAC FIFO */
+ sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR);
+ sky2_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
+}
+
+static void sky2_ramset(struct sky2_hw *hw, u16 q, u32 start, size_t len)
+{
+ u32 end;
+
+ start /= 8;
+ len /= 8;
+ end = start + len - 1;
+
+ sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR);
+ sky2_write32(hw, RB_ADDR(q, RB_START), start);
+ sky2_write32(hw, RB_ADDR(q, RB_END), end);
+ sky2_write32(hw, RB_ADDR(q, RB_WP), start);
+ sky2_write32(hw, RB_ADDR(q, RB_RP), start);
+
+ if (q == Q_R1 || q == Q_R2) {
+ u32 rxup, rxlo;
+
+ rxlo = len/2;
+ rxup = rxlo + len/4;
+
+ /* Set thresholds on receive queue's */
+ sky2_write32(hw, RB_ADDR(q, RB_RX_UTPP), rxup);
+ sky2_write32(hw, RB_ADDR(q, RB_RX_LTPP), rxlo);
+ } else {
+ /* Enable store & forward on Tx queue's because
+ * Tx FIFO is only 1K on Yukon
+ */
+ sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD);
+ }
+
+ sky2_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD);
+ sky2_read8(hw, RB_ADDR(q, RB_CTRL));
+}
+
+/* Setup Bus Memory Interface */
+static void sky2_qset(struct sky2_hw *hw, u16 q, u32 wm)
+{
+ sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_RESET);
+ sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_OPER_INIT);
+ sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_FIFO_OP_ON);
+ sky2_write32(hw, Q_ADDR(q, Q_WM), wm);
+}
+
+/* Setup prefetch unit registers. This is the interface between
+ * hardware and driver list elements
+ */
+static inline void sky2_prefetch_init(struct sky2_hw *hw, u32 qaddr,
+ u64 addr, u32 last)
+{
+ sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+ sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_RST_CLR);
+ sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_HI), addr >> 32);
+ sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_ADDR_LO), (u32) addr);
+ sky2_write16(hw, Y2_QADDR(qaddr, PREF_UNIT_LAST_IDX), last);
+ sky2_write32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL), PREF_UNIT_OP_ON);
+
+ sky2_read32(hw, Y2_QADDR(qaddr, PREF_UNIT_CTRL));
+}
+
+static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2)
+{
+ struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod;
+
+ sky2->tx_prod = (sky2->tx_prod + 1) % TX_RING_SIZE;
+ return le;
+}
+
+/*
+ * This is a workaround code taken from SysKonnect sk98lin driver
+ * to deal with chip bug on Yukon EC rev 0 in the wraparound case.
+ */
+static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q,
+ u16 idx, u16 *last, u16 size)
+{
+ if (is_ec_a1(hw) && idx < *last) {
+ u16 hwget = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX));
+
+ if (hwget == 0) {
+ /* Start prefetching again */
+ sky2_write8(hw, Y2_QADDR(q, PREF_UNIT_FIFO_WM), 0xe0);
+ goto setnew;
+ }
+
+ if (hwget == size - 1) {
+ /* set watermark to one list element */
+ sky2_write8(hw, Y2_QADDR(q, PREF_UNIT_FIFO_WM), 8);
+
+ /* set put index to first list element */
+ sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), 0);
+ } else /* have hardware go to end of list */
+ sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX),
+ size - 1);
+ } else {
+setnew:
+ sky2_write16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX), idx);
+ }
+ *last = idx;
+}
+
+
+static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2)
+{
+ struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put;
+ sky2->rx_put = (sky2->rx_put + 1) % RX_LE_SIZE;
+ return le;
+}
+
+/* Build description to hardware about buffer */
+static inline void sky2_rx_add(struct sky2_port *sky2, struct ring_info *re)
+{
+ struct sky2_rx_le *le;
+ u32 hi = (re->mapaddr >> 16) >> 16;
+
+ re->idx = sky2->rx_put;
+ if (sky2->rx_addr64 != hi) {
+ le = sky2_next_rx(sky2);
+ le->addr = cpu_to_le32(hi);
+ le->ctrl = 0;
+ le->opcode = OP_ADDR64 | HW_OWNER;
+ sky2->rx_addr64 = hi;
+ }
+
+ le = sky2_next_rx(sky2);
+ le->addr = cpu_to_le32((u32) re->mapaddr);
+ le->length = cpu_to_le16(re->maplen);
+ le->ctrl = 0;
+ le->opcode = OP_PACKET | HW_OWNER;
+}
+
+
+/* Tell chip where to start receive checksum.
+ * Actually has two checksums, but set both same to avoid possible byte
+ * order problems.
+ */
+static void rx_set_checksum(struct sky2_port *sky2)
+{
+ struct sky2_rx_le *le;
+
+ le = sky2_next_rx(sky2);
+ le->addr = (ETH_HLEN << 16) | ETH_HLEN;
+ le->ctrl = 0;
+ le->opcode = OP_TCPSTART | HW_OWNER;
+
+ sky2_write32(sky2->hw,
+ Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+ sky2->rx_csum ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
+
+}
+
+/*
+ * The RX Stop command will not work for Yukon-2 if the BMU does not
+ * reach the end of packet and since we can't make sure that we have
+ * incoming data, we must reset the BMU while it is not doing a DMA
+ * transfer. Since it is possible that the RX path is still active,
+ * the RX RAM buffer will be stopped first, so any possible incoming
+ * data will not trigger a DMA. After the RAM buffer is stopped, the
+ * BMU is polled until any DMA in progress is ended and only then it
+ * will be reset.
+ */
+static void sky2_rx_stop(struct sky2_port *sky2)
+{
+ struct sky2_hw *hw = sky2->hw;
+ unsigned rxq = rxqaddr[sky2->port];
+ int i;
+
+ /* disable the RAM Buffer receive queue */
+ sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_DIS_OP_MD);
+
+ for (i = 0; i < 0xffff; i++)
+ if (sky2_read8(hw, RB_ADDR(rxq, Q_RSL))
+ == sky2_read8(hw, RB_ADDR(rxq, Q_RL)))
+ goto stopped;
+
+ printk(KERN_WARNING PFX "%s: receiver stop failed\n",
+ sky2->netdev->name);
+stopped:
+ sky2_write32(hw, Q_ADDR(rxq, Q_CSR), BMU_RST_SET | BMU_FIFO_RST);
+
+ /* reset the Rx prefetch unit */
+ sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
+}
+
+/* Clean out receive buffer area, assumes receiver hardware stopped */
+static void sky2_rx_clean(struct sky2_port *sky2)
+{
+ unsigned i;
+
+ memset(sky2->rx_le, 0, RX_LE_BYTES);
+ for (i = 0; i < sky2->rx_pending; i++) {
+ struct ring_info *re = sky2->rx_ring + i;
+
+ if (re->skb) {
+ pci_unmap_single(sky2->hw->pdev,
+ re->mapaddr, re->maplen,
+ PCI_DMA_FROMDEVICE);
+ kfree_skb(re->skb);
+ re->skb = NULL;
+ }
+ }
+}
+
+#ifdef SKY2_VLAN_TAG_USED
+static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ u16 port = sky2->port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sky2->tx_lock, flags);
+
+ sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON);
+ sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON);
+ sky2->vlgrp = grp;
+
+ spin_unlock_irqrestore(&sky2->tx_lock, flags);
+}
+
+static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ u16 port = sky2->port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sky2->tx_lock, flags);
+
+ sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF);
+ sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF);
+ if (sky2->vlgrp)
+ sky2->vlgrp->vlan_devices[vid] = NULL;
+
+ spin_unlock_irqrestore(&sky2->tx_lock, flags);
+}
+#endif
+
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+static inline unsigned rx_size(const struct sky2_port *sky2)
+{
+ return roundup(sky2->netdev->mtu + ETH_HLEN + 4, 8);
+}
+
+/*
+ * Allocate and setup receiver buffer pool.
+ * In case of 64 bit dma, there are 2X as many list elements
+ * available as ring entries
+ * and need to reserve one list element so we don't wrap around.
+ *
+ * It appears the hardware has a bug in the FIFO logic that
+ * cause it to hang if the FIFO gets overrun and the receive buffer
+ * is not aligned. This means we can't use skb_reserve to align
+ * the IP header.
+ */
+static int sky2_rx_start(struct sky2_port *sky2)
+{
+ struct sky2_hw *hw = sky2->hw;
+ unsigned size = rx_size(sky2);
+ unsigned rxq = rxqaddr[sky2->port];
+ int i;
+
+ sky2->rx_put = sky2->rx_next = 0;
+ sky2_qset(hw, rxq, is_pciex(hw) ? 0x80 : 0x600);
+ sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1);
+
+ rx_set_checksum(sky2);
+ for (i = 0; i < sky2->rx_pending; i++) {
+ struct ring_info *re = sky2->rx_ring + i;
+
+ re->skb = dev_alloc_skb(size);
+ if (!re->skb)
+ goto nomem;
+
+ re->mapaddr = pci_map_single(hw->pdev, re->skb->data,
+ size, PCI_DMA_FROMDEVICE);
+ re->maplen = size;
+ sky2_rx_add(sky2, re);
+ }
+
+ /* Tell chip about available buffers */
+ sky2_write16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX), sky2->rx_put);
+ sky2->rx_last_put = sky2_read16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX));
+ return 0;
+nomem:
+ sky2_rx_clean(sky2);
+ return -ENOMEM;
+}
+
+/* Bring up network interface. */
+static int sky2_up(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ u32 ramsize, rxspace;
+ int err = -ENOMEM;
+
+ if (netif_msg_ifup(sky2))
+ printk(KERN_INFO PFX "%s: enabling interface\n", dev->name);
+
+ /* must be power of 2 */
+ sky2->tx_le = pci_alloc_consistent(hw->pdev,
+ TX_RING_SIZE *
+ sizeof(struct sky2_tx_le),
+ &sky2->tx_le_map);
+ if (!sky2->tx_le)
+ goto err_out;
+
+ sky2->tx_ring = kzalloc(TX_RING_SIZE * sizeof(struct ring_info),
+ GFP_KERNEL);
+ if (!sky2->tx_ring)
+ goto err_out;
+ sky2->tx_prod = sky2->tx_cons = 0;
+
+ sky2->rx_le = pci_alloc_consistent(hw->pdev, RX_LE_BYTES,
+ &sky2->rx_le_map);
+ if (!sky2->rx_le)
+ goto err_out;
+ memset(sky2->rx_le, 0, RX_LE_BYTES);
+
+ sky2->rx_ring = kzalloc(sky2->rx_pending * sizeof(struct ring_info),
+ GFP_KERNEL);
+ if (!sky2->rx_ring)
+ goto err_out;
+
+ sky2_mac_init(hw, port);
+
+ /* Configure RAM buffers */
+ if (hw->chip_id == CHIP_ID_YUKON_FE ||
+ (hw->chip_id == CHIP_ID_YUKON_EC && hw->chip_rev == 2))
+ ramsize = 4096;
+ else {
+ u8 e0 = sky2_read8(hw, B2_E_0);
+ ramsize = (e0 == 0) ? (128 * 1024) : (e0 * 4096);
+ }
+
+ /* 2/3 for Rx */
+ rxspace = (2 * ramsize) / 3;
+ sky2_ramset(hw, rxqaddr[port], 0, rxspace);
+ sky2_ramset(hw, txqaddr[port], rxspace, ramsize - rxspace);
+
+ /* Make sure SyncQ is disabled */
+ sky2_write8(hw, RB_ADDR(port == 0 ? Q_XS1 : Q_XS2, RB_CTRL),
+ RB_RST_SET);
+
+ sky2_qset(hw, txqaddr[port], 0x600);
+ sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
+ TX_RING_SIZE - 1);
+
+ err = sky2_rx_start(sky2);
+ if (err)
+ goto err_out;
+
+ /* Enable interrupts from phy/mac for port */
+ hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2;
+ sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ return 0;
+
+err_out:
+ if (sky2->rx_le)
+ pci_free_consistent(hw->pdev, RX_LE_BYTES,
+ sky2->rx_le, sky2->rx_le_map);
+ if (sky2->tx_le)
+ pci_free_consistent(hw->pdev,
+ TX_RING_SIZE * sizeof(struct sky2_tx_le),
+ sky2->tx_le, sky2->tx_le_map);
+ if (sky2->tx_ring)
+ kfree(sky2->tx_ring);
+ if (sky2->rx_ring)
+ kfree(sky2->rx_ring);
+
+ return err;
+}
+
+/* Modular subtraction in ring */
+static inline int tx_dist(unsigned tail, unsigned head)
+{
+ return (head >= tail ? head : head + TX_RING_SIZE) - tail;
+}
+
+/* Number of list elements available for next tx */
+static inline int tx_avail(const struct sky2_port *sky2)
+{
+ return sky2->tx_pending - tx_dist(sky2->tx_cons, sky2->tx_prod);
+}
+
+/* Estimate of number of transmit list elements required */
+static inline unsigned tx_le_req(const struct sk_buff *skb)
+{
+ unsigned count;
+
+ count = sizeof(dma_addr_t) / sizeof(u32);
+ count += skb_shinfo(skb)->nr_frags * count;
+
+ if (skb_shinfo(skb)->tso_size)
+ ++count;
+
+ if (skb->ip_summed)
+ ++count;
+
+ return count;
+}
+
+/*
+ * Put one packet in ring for transmit.
+ * A single packet can generate multiple list elements, and
+ * the number of ring elements will probably be less than the number
+ * of list elements used.
+ */
+static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ struct sky2_tx_le *le = NULL;
+ struct ring_info *re;
+ unsigned long flags;
+ unsigned i, len;
+ dma_addr_t mapping;
+ u32 addr64;
+ u16 mss;
+ u8 ctrl;
+
+ local_irq_save(flags);
+ if (!spin_trylock(&sky2->tx_lock)) {
+ local_irq_restore(flags);
+ return NETDEV_TX_LOCKED;
+ }
+
+ if (unlikely(tx_avail(sky2) < tx_le_req(skb))) {
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&sky2->tx_lock, flags);
+
+ printk(KERN_WARNING PFX "%s: ring full when queue awake!\n",
+ dev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ if (unlikely(netif_msg_tx_queued(sky2)))
+ printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n",
+ dev->name, sky2->tx_prod, skb->len);
+
+ len = skb_headlen(skb);
+ mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE);
+ addr64 = (mapping >> 16) >> 16;
+
+ re = sky2->tx_ring + sky2->tx_prod;
+
+ /* Send high bits if changed */
+ if (addr64 != sky2->tx_addr64) {
+ le = get_tx_le(sky2);
+ le->tx.addr = cpu_to_le32(addr64);
+ le->ctrl = 0;
+ le->opcode = OP_ADDR64 | HW_OWNER;
+ sky2->tx_addr64 = addr64;
+ }
+
+ /* Check for TCP Segmentation Offload */
+ mss = skb_shinfo(skb)->tso_size;
+ if (mss != 0) {
+ /* just drop the packet if non-linear expansion fails */
+ if (skb_header_cloned(skb) &&
+ pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
+ dev_kfree_skb_any(skb);
+ goto out_unlock;
+ }
+
+ mss += ((skb->h.th->doff - 5) * 4); /* TCP options */
+ mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr);
+ mss += ETH_HLEN;
+ }
+
+ if (mss != sky2->tx_last_mss) {
+ le = get_tx_le(sky2);
+ le->tx.tso.size = cpu_to_le16(mss);
+ le->tx.tso.rsvd = 0;
+ le->opcode = OP_LRGLEN | HW_OWNER;
+ le->ctrl = 0;
+ sky2->tx_last_mss = mss;
+ }
+
+ ctrl = 0;
+#ifdef SKY2_VLAN_TAG_USED
+ /* Add VLAN tag, can piggyback on LRGLEN or ADDR64 */
+ if (sky2->vlgrp && vlan_tx_tag_present(skb)) {
+ if (!le) {
+ le = get_tx_le(sky2);
+ le->tx.addr = 0;
+ le->opcode = OP_VLAN|HW_OWNER;
+ le->ctrl = 0;
+ } else
+ le->opcode |= OP_VLAN;
+ le->length = cpu_to_be16(vlan_tx_tag_get(skb));
+ ctrl |= INS_VLAN;
+ }
+#endif
+
+ /* Handle TCP checksum offload */
+ if (skb->ip_summed == CHECKSUM_HW) {
+ u16 hdr = skb->h.raw - skb->data;
+ u16 offset = hdr + skb->csum;
+
+ ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
+ if (skb->nh.iph->protocol == IPPROTO_UDP)
+ ctrl |= UDPTCP;
+
+ le = get_tx_le(sky2);
+ le->tx.csum.start = cpu_to_le16(hdr);
+ le->tx.csum.offset = cpu_to_le16(offset);
+ le->length = 0; /* initial checksum value */
+ le->ctrl = 1; /* one packet */
+ le->opcode = OP_TCPLISW | HW_OWNER;
+ }
+
+ le = get_tx_le(sky2);
+ le->tx.addr = cpu_to_le32((u32) mapping);
+ le->length = cpu_to_le16(len);
+ le->ctrl = ctrl;
+ le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER);
+
+ /* Record the transmit mapping info */
+ re->skb = skb;
+ re->mapaddr = mapping;
+ re->maplen = len;
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ struct ring_info *fre;
+
+ mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset,
+ frag->size, PCI_DMA_TODEVICE);
+ addr64 = (mapping >> 16) >> 16;
+ if (addr64 != sky2->tx_addr64) {
+ le = get_tx_le(sky2);
+ le->tx.addr = cpu_to_le32(addr64);
+ le->ctrl = 0;
+ le->opcode = OP_ADDR64 | HW_OWNER;
+ sky2->tx_addr64 = addr64;
+ }
+
+ le = get_tx_le(sky2);
+ le->tx.addr = cpu_to_le32((u32) mapping);
+ le->length = cpu_to_le16(frag->size);
+ le->ctrl = ctrl;
+ le->opcode = OP_BUFFER | HW_OWNER;
+
+ fre = sky2->tx_ring
+ + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE;
+ fre->skb = NULL;
+ fre->mapaddr = mapping;
+ fre->maplen = frag->size;
+ }
+ re->idx = sky2->tx_prod;
+ le->ctrl |= EOP;
+
+ sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod,
+ &sky2->tx_last_put, TX_RING_SIZE);
+
+ if (tx_avail(sky2) < MAX_SKB_TX_LE + 1)
+ netif_stop_queue(dev);
+
+out_unlock:
+ mmiowb();
+ spin_unlock_irqrestore(&sky2->tx_lock, flags);
+
+ dev->trans_start = jiffies;
+ return NETDEV_TX_OK;
+}
+
+/*
+ * Free ring elements from starting at tx_cons until "done"
+ *
+ * NB: the hardware will tell us about partial completion of multi-part
+ * buffers; these are deferred until completion.
+ */
+static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
+{
+ struct net_device *dev = sky2->netdev;
+ unsigned i;
+
+ if (unlikely(netif_msg_tx_done(sky2)))
+ printk(KERN_DEBUG "%s: tx done, up to %u\n",
+ dev->name, done);
+
+ spin_lock(&sky2->tx_lock);
+
+ while (sky2->tx_cons != done) {
+ struct ring_info *re = sky2->tx_ring + sky2->tx_cons;
+ struct sk_buff *skb;
+
+ /* Check for partial status */
+ if (tx_dist(sky2->tx_cons, done)
+ < tx_dist(sky2->tx_cons, re->idx))
+ goto out;
+
+ skb = re->skb;
+ pci_unmap_single(sky2->hw->pdev,
+ re->mapaddr, re->maplen, PCI_DMA_TODEVICE);
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ struct ring_info *fre;
+ fre =
+ sky2->tx_ring + (sky2->tx_cons + i +
+ 1) % TX_RING_SIZE;
+ pci_unmap_page(sky2->hw->pdev, fre->mapaddr,
+ fre->maplen, PCI_DMA_TODEVICE);
+ }
+
+ dev_kfree_skb_any(skb);
+
+ sky2->tx_cons = re->idx;
+ }
+out:
+
+ if (netif_queue_stopped(dev) && tx_avail(sky2) > MAX_SKB_TX_LE)
+ netif_wake_queue(dev);
+ spin_unlock(&sky2->tx_lock);
+}
+
+/* Cleanup all untransmitted buffers, assume transmitter not running */
+static inline void sky2_tx_clean(struct sky2_port *sky2)
+{
+ sky2_tx_complete(sky2, sky2->tx_prod);
+}
+
+/* Network shutdown */
+static int sky2_down(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ u16 ctrl;
+
+ if (netif_msg_ifdown(sky2))
+ printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
+
+ netif_stop_queue(dev);
+
+ sky2_phy_reset(hw, port);
+
+ /* Stop transmitter */
+ sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_STOP);
+ sky2_read32(hw, Q_ADDR(txqaddr[port], Q_CSR));
+
+ sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
+ RB_RST_SET | RB_DIS_OP_MD);
+
+ ctrl = gma_read16(hw, port, GM_GP_CTRL);
+ ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA);
+ gma_write16(hw, port, GM_GP_CTRL, ctrl);
+
+ sky2_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
+
+ /* Workaround shared GMAC reset */
+ if (!(hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0
+ && port == 0 && hw->dev[1] && netif_running(hw->dev[1])))
+ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_SET);
+
+ /* Disable Force Sync bit and Enable Alloc bit */
+ sky2_write8(hw, SK_REG(port, TXA_CTRL),
+ TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC);
+
+ /* Stop Interval Timer and Limit Counter of Tx Arbiter */
+ sky2_write32(hw, SK_REG(port, TXA_ITI_INI), 0L);
+ sky2_write32(hw, SK_REG(port, TXA_LIM_INI), 0L);
+
+ /* Reset the PCI FIFO of the async Tx queue */
+ sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR),
+ BMU_RST_SET | BMU_FIFO_RST);
+
+ /* Reset the Tx prefetch units */
+ sky2_write32(hw, Y2_QADDR(txqaddr[port], PREF_UNIT_CTRL),
+ PREF_UNIT_RST_SET);
+
+ sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
+
+ sky2_rx_stop(sky2);
+
+ sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
+ sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
+
+ /* turn off LED's */
+ sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
+
+ sky2_tx_clean(sky2);
+ sky2_rx_clean(sky2);
+
+ pci_free_consistent(hw->pdev, RX_LE_BYTES,
+ sky2->rx_le, sky2->rx_le_map);
+ kfree(sky2->rx_ring);
+
+ pci_free_consistent(hw->pdev,
+ TX_RING_SIZE * sizeof(struct sky2_tx_le),
+ sky2->tx_le, sky2->tx_le_map);
+ kfree(sky2->tx_ring);
+
+ return 0;
+}
+
+static u16 sky2_phy_speed(const struct sky2_hw *hw, u16 aux)
+{
+ if (!hw->copper)
+ return SPEED_1000;
+
+ if (hw->chip_id == CHIP_ID_YUKON_FE)
+ return (aux & PHY_M_PS_SPEED_100) ? SPEED_100 : SPEED_10;
+
+ switch (aux & PHY_M_PS_SPEED_MSK) {
+ case PHY_M_PS_SPEED_1000:
+ return SPEED_1000;
+ case PHY_M_PS_SPEED_100:
+ return SPEED_100;
+ default:
+ return SPEED_10;
+ }
+}
+
+static void sky2_link_up(struct sky2_port *sky2)
+{
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ u16 reg;
+
+ /* disable Rx GMAC FIFO flush mode */
+ sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RX_F_FL_OFF);
+
+ /* Enable Transmit FIFO Underrun */
+ sky2_write8(hw, SK_REG(port, GMAC_IRQ_MSK), GMAC_DEF_MSK);
+
+ reg = gma_read16(hw, port, GM_GP_CTRL);
+ if (sky2->duplex == DUPLEX_FULL || sky2->autoneg == AUTONEG_ENABLE)
+ reg |= GM_GPCR_DUP_FULL;
+
+ /* enable Rx/Tx */
+ reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA;
+ gma_write16(hw, port, GM_GP_CTRL, reg);
+ gma_read16(hw, port, GM_GP_CTRL);
+
+ gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK);
+
+ netif_carrier_on(sky2->netdev);
+ netif_wake_queue(sky2->netdev);
+
+ /* Turn on link LED */
+ sky2_write8(hw, SK_REG(port, LNK_LED_REG),
+ LINKLED_ON | LINKLED_BLINK_OFF | LINKLED_LINKSYNC_OFF);
+
+ if (hw->chip_id == CHIP_ID_YUKON_XL) {
+ u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */
+ PHY_M_LEDC_INIT_CTRL(sky2->speed ==
+ SPEED_10 ? 7 : 0) |
+ PHY_M_LEDC_STA1_CTRL(sky2->speed ==
+ SPEED_100 ? 7 : 0) |
+ PHY_M_LEDC_STA0_CTRL(sky2->speed ==
+ SPEED_1000 ? 7 : 0));
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+ }
+
+ if (netif_msg_link(sky2))
+ printk(KERN_INFO PFX
+ "%s: Link is up at %d Mbps, %s duplex, flow control %s\n",
+ sky2->netdev->name, sky2->speed,
+ sky2->duplex == DUPLEX_FULL ? "full" : "half",
+ (sky2->tx_pause && sky2->rx_pause) ? "both" :
+ sky2->tx_pause ? "tx" : sky2->rx_pause ? "rx" : "none");
+}
+
+static void sky2_link_down(struct sky2_port *sky2)
+{
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ u16 reg;
+
+ gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);
+
+ reg = gma_read16(hw, port, GM_GP_CTRL);
+ reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA);
+ gma_write16(hw, port, GM_GP_CTRL, reg);
+ gma_read16(hw, port, GM_GP_CTRL); /* PCI post */
+
+ if (sky2->rx_pause && !sky2->tx_pause) {
+ /* restore Asymmetric Pause bit */
+ gm_phy_write(hw, port, PHY_MARV_AUNE_ADV,
+ gm_phy_read(hw, port, PHY_MARV_AUNE_ADV)
+ | PHY_M_AN_ASP);
+ }
+
+ sky2_phy_reset(hw, port);
+
+ netif_carrier_off(sky2->netdev);
+ netif_stop_queue(sky2->netdev);
+
+ /* Turn on link LED */
+ sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
+
+ if (netif_msg_link(sky2))
+ printk(KERN_INFO PFX "%s: Link is down.\n", sky2->netdev->name);
+ sky2_phy_init(hw, port);
+}
+
+static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
+{
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ u16 lpa;
+
+ lpa = gm_phy_read(hw, port, PHY_MARV_AUNE_LP);
+
+ if (lpa & PHY_M_AN_RF) {
+ printk(KERN_ERR PFX "%s: remote fault", sky2->netdev->name);
+ return -1;
+ }
+
+ if (hw->chip_id != CHIP_ID_YUKON_FE &&
+ gm_phy_read(hw, port, PHY_MARV_1000T_STAT) & PHY_B_1000S_MSF) {
+ printk(KERN_ERR PFX "%s: master/slave fault",
+ sky2->netdev->name);
+ return -1;
+ }
+
+ if (!(aux & PHY_M_PS_SPDUP_RES)) {
+ printk(KERN_ERR PFX "%s: speed/duplex mismatch",
+ sky2->netdev->name);
+ return -1;
+ }
+
+ sky2->duplex = (aux & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
+
+ sky2->speed = sky2_phy_speed(hw, aux);
+
+ /* Pause bits are offset (9..8) */
+ if (hw->chip_id == CHIP_ID_YUKON_XL)
+ aux >>= 6;
+
+ sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0;
+ sky2->tx_pause = (aux & PHY_M_PS_TX_P_EN) != 0;
+
+ if ((sky2->tx_pause || sky2->rx_pause)
+ && !(sky2->speed < SPEED_1000 && sky2->duplex == DUPLEX_HALF))
+ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON);
+ else
+ sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
+
+ return 0;
+}
+
+/*
+ * Interrupt from PHY are handled in tasklet (soft irq)
+ * because accessing phy registers requires spin wait which might
+ * cause excess interrupt latency.
+ */
+static void sky2_phy_task(unsigned long data)
+{
+ struct sky2_port *sky2 = (struct sky2_port *)data;
+ struct sky2_hw *hw = sky2->hw;
+ u16 istatus, phystat;
+
+ spin_lock(&hw->phy_lock);
+ istatus = gm_phy_read(hw, sky2->port, PHY_MARV_INT_STAT);
+ phystat = gm_phy_read(hw, sky2->port, PHY_MARV_PHY_STAT);
+
+ if (netif_msg_intr(sky2))
+ printk(KERN_INFO PFX "%s: phy interrupt status 0x%x 0x%x\n",
+ sky2->netdev->name, istatus, phystat);
+
+ if (istatus & PHY_M_IS_AN_COMPL) {
+ if (sky2_autoneg_done(sky2, phystat) == 0)
+ sky2_link_up(sky2);
+ goto out;
+ }
+
+ if (istatus & PHY_M_IS_LSP_CHANGE)
+ sky2->speed = sky2_phy_speed(hw, phystat);
+
+ if (istatus & PHY_M_IS_DUP_CHANGE)
+ sky2->duplex =
+ (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
+
+ if (istatus & PHY_M_IS_LST_CHANGE) {
+ if (phystat & PHY_M_PS_LINK_UP)
+ sky2_link_up(sky2);
+ else
+ sky2_link_down(sky2);
+ }
+out:
+ spin_unlock(&hw->phy_lock);
+
+ local_irq_disable();
+ hw->intr_mask |= (sky2->port == 0) ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2;
+ sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ local_irq_enable();
+}
+
+static void sky2_tx_timeout(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ if (netif_msg_timer(sky2))
+ printk(KERN_ERR PFX "%s: tx timeout\n", dev->name);
+
+ sky2_write32(sky2->hw, Q_ADDR(txqaddr[sky2->port], Q_CSR), BMU_STOP);
+ sky2_read32(sky2->hw, Q_ADDR(txqaddr[sky2->port], Q_CSR));
+
+ sky2_tx_clean(sky2);
+}
+
+static int sky2_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ int err;
+ u16 ctl, mode;
+
+ if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
+ return -EINVAL;
+
+ if (!netif_running(dev)) {
+ dev->mtu = new_mtu;
+ return 0;
+ }
+
+ local_irq_disable();
+ sky2_write32(hw, B0_IMSK, 0);
+
+ ctl = gma_read16(hw, sky2->port, GM_GP_CTRL);
+ gma_write16(hw, sky2->port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA);
+ sky2_rx_stop(sky2);
+ sky2_rx_clean(sky2);
+
+ dev->mtu = new_mtu;
+ mode = DATA_BLIND_VAL(DATA_BLIND_DEF) |
+ GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
+
+ if (dev->mtu > ETH_DATA_LEN)
+ mode |= GM_SMOD_JUMBO_ENA;
+
+ gma_write16(hw, sky2->port, GM_SERIAL_MODE, mode);
+
+ sky2_write8(hw, RB_ADDR(rxqaddr[sky2->port], RB_CTRL), RB_ENA_OP_MD);
+
+ err = sky2_rx_start(sky2);
+ gma_write16(hw, sky2->port, GM_GP_CTRL, ctl);
+
+ sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ sky2_read32(hw, B0_IMSK);
+ local_irq_enable();
+ return err;
+}
+
+/*
+ * Receive one packet.
+ * For small packets or errors, just reuse existing skb.
+ * For larger packets, get new buffer.
+ */
+static struct sk_buff *sky2_receive(struct sky2_port *sky2,
+ u16 length, u32 status)
+{
+ struct ring_info *re = sky2->rx_ring + sky2->rx_next;
+ struct sk_buff *skb = NULL;
+ struct net_device *dev;
+ const unsigned int bufsize = rx_size(sky2);
+
+ if (unlikely(netif_msg_rx_status(sky2)))
+ printk(KERN_DEBUG PFX "%s: rx slot %u status 0x%x len %d\n",
+ sky2->netdev->name, sky2->rx_next, status, length);
+
+ sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending;
+
+ if (!(status & GMR_FS_RX_OK) || (status & GMR_FS_ANY_ERR))
+ goto error;
+
+ if (length < RX_COPY_THRESHOLD) {
+ skb = alloc_skb(length + 2, GFP_ATOMIC);
+ if (!skb)
+ goto resubmit;
+
+ skb_reserve(skb, 2);
+ pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->mapaddr,
+ length, PCI_DMA_FROMDEVICE);
+ memcpy(skb->data, re->skb->data, length);
+ skb->ip_summed = re->skb->ip_summed;
+ skb->csum = re->skb->csum;
+ pci_dma_sync_single_for_device(sky2->hw->pdev, re->mapaddr,
+ length, PCI_DMA_FROMDEVICE);
+ } else {
+ struct sk_buff *nskb;
+
+ nskb = dev_alloc_skb(bufsize);
+ if (!nskb)
+ goto resubmit;
+
+ skb = re->skb;
+ re->skb = nskb;
+ pci_unmap_single(sky2->hw->pdev, re->mapaddr,
+ re->maplen, PCI_DMA_FROMDEVICE);
+ prefetch(skb->data);
+
+ re->mapaddr = pci_map_single(sky2->hw->pdev, nskb->data,
+ bufsize, PCI_DMA_FROMDEVICE);
+ re->maplen = bufsize;
+ }
+
+ skb_put(skb, length);
+ dev = sky2->netdev;
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ dev->last_rx = jiffies;
+
+resubmit:
+ re->skb->ip_summed = CHECKSUM_NONE;
+ sky2_rx_add(sky2, re);
+
+ /* Tell receiver about new buffers. */
+ sky2_put_idx(sky2->hw, rxqaddr[sky2->port], sky2->rx_put,
+ &sky2->rx_last_put, RX_LE_SIZE);
+
+ return skb;
+
+error:
+ if (status & GMR_FS_GOOD_FC)
+ goto resubmit;
+
+ if (netif_msg_rx_err(sky2))
+ printk(KERN_INFO PFX "%s: rx error, status 0x%x length %d\n",
+ sky2->netdev->name, status, length);
+
+ if (status & (GMR_FS_LONG_ERR | GMR_FS_UN_SIZE))
+ sky2->net_stats.rx_length_errors++;
+ if (status & GMR_FS_FRAGMENT)
+ sky2->net_stats.rx_frame_errors++;
+ if (status & GMR_FS_CRC_ERR)
+ sky2->net_stats.rx_crc_errors++;
+ if (status & GMR_FS_RX_FF_OV)
+ sky2->net_stats.rx_fifo_errors++;
+
+ goto resubmit;
+}
+
+/* Transmit ring index in reported status block is encoded as:
+ *
+ * | TXS2 | TXA2 | TXS1 | TXA1
+ */
+static inline u16 tx_index(u8 port, u32 status, u16 len)
+{
+ if (port == 0)
+ return status & 0xfff;
+ else
+ return ((status >> 24) & 0xff) | (len & 0xf) << 8;
+}
+
+/*
+ * Both ports share the same status interrupt, therefore there is only
+ * one poll routine.
+ */
+static int sky2_poll(struct net_device *dev0, int *budget)
+{
+ struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw;
+ unsigned int to_do = min(dev0->quota, *budget);
+ unsigned int work_done = 0;
+ u16 hwidx;
+
+ hwidx = sky2_read16(hw, STAT_PUT_IDX);
+ BUG_ON(hwidx >= STATUS_RING_SIZE);
+ rmb();
+
+ do {
+ struct sky2_status_le *le = hw->st_le + hw->st_idx;
+ struct sky2_port *sky2;
+ struct sk_buff *skb;
+ u32 status;
+ u16 length;
+
+ /* Are we done yet? */
+ if (hw->st_idx == hwidx) {
+ sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
+ hwidx = sky2_read16(hw, STAT_PUT_IDX);
+ if (hwidx == hw->st_idx)
+ break;
+ }
+
+ hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE;
+ prefetch(&hw->st_le[hw->st_idx]);
+
+ BUG_ON(le->link >= hw->ports || !hw->dev[le->link]);
+
+ sky2 = netdev_priv(hw->dev[le->link]);
+ status = le32_to_cpu(le->status);
+ length = le16_to_cpu(le->length);
+
+ switch (le->opcode & ~HW_OWNER) {
+ case OP_RXSTAT:
+ skb = sky2_receive(sky2, length, status);
+ if (!skb)
+ break;
+#ifdef SKY2_VLAN_TAG_USED
+ if (sky2->vlgrp && (status & GMR_FS_VLAN)) {
+ vlan_hwaccel_receive_skb(skb,
+ sky2->vlgrp,
+ be16_to_cpu(sky2->rx_tag));
+ } else
+#endif
+ netif_receive_skb(skb);
+ ++work_done;
+ break;
+
+#ifdef SKY2_VLAN_TAG_USED
+ case OP_RXVLAN:
+ sky2->rx_tag = length;
+ break;
+
+ case OP_RXCHKSVLAN:
+ sky2->rx_tag = length;
+ /* fall through */
+#endif
+ case OP_RXCHKS:
+ skb = sky2->rx_ring[sky2->rx_next].skb;
+ skb->ip_summed = CHECKSUM_HW;
+ skb->csum = le16_to_cpu(status);
+ break;
+
+ case OP_TXINDEXLE:
+ sky2_tx_complete(sky2,
+ tx_index(sky2->port, status, length));
+ break;
+
+ default:
+ if (net_ratelimit())
+ printk(KERN_WARNING PFX
+ "unknown status opcode 0x%x\n",
+ le->opcode);
+ break;
+ }
+
+ le->opcode = 0; /* paranoia */
+ } while (work_done < to_do);
+
+ mmiowb();
+
+ *budget -= work_done;
+ dev0->quota -= work_done;
+ if (work_done < to_do) {
+ /*
+ * Another chip workaround, need to restart TX timer if status
+ * LE was handled. WA_DEV_43_418
+ */
+ if (is_ec_a1(hw)) {
+ sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP);
+ sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
+ }
+
+ netif_rx_complete(dev0);
+ hw->intr_mask |= Y2_IS_STAT_BMU;
+ sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ sky2_read32(hw, B0_IMSK);
+ }
+
+ return work_done >= to_do;
+
+}
+
+static void sky2_hw_error(struct sky2_hw *hw, unsigned port, u32 status)
+{
+ struct net_device *dev = hw->dev[port];
+
+ printk(KERN_INFO PFX "%s: hw error interrupt status 0x%x\n",
+ dev->name, status);
+
+ if (status & Y2_IS_PAR_RD1) {
+ printk(KERN_ERR PFX "%s: ram data read parity error\n",
+ dev->name);
+ /* Clear IRQ */
+ sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_RD_PERR);
+ }
+
+ if (status & Y2_IS_PAR_WR1) {
+ printk(KERN_ERR PFX "%s: ram data write parity error\n",
+ dev->name);
+
+ sky2_write16(hw, RAM_BUFFER(port, B3_RI_CTRL), RI_CLR_WR_PERR);
+ }
+
+ if (status & Y2_IS_PAR_MAC1) {
+ printk(KERN_ERR PFX "%s: MAC parity error\n", dev->name);
+ sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_PE);
+ }
+
+ if (status & Y2_IS_PAR_RX1) {
+ printk(KERN_ERR PFX "%s: RX parity error\n", dev->name);
+ sky2_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), BMU_CLR_IRQ_PAR);
+ }
+
+ if (status & Y2_IS_TCP_TXA1) {
+ printk(KERN_ERR PFX "%s: TCP segmentation error\n", dev->name);
+ sky2_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), BMU_CLR_IRQ_TCP);
+ }
+}
+
+static void sky2_hw_intr(struct sky2_hw *hw)
+{
+ u32 status = sky2_read32(hw, B0_HWE_ISRC);
+
+ if (status & Y2_IS_TIST_OV)
+ sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
+
+ if (status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) {
+ u16 pci_err;
+
+ pci_read_config_word(hw->pdev, PCI_STATUS, &pci_err);
+ printk(KERN_ERR PFX "%s: pci hw error (0x%x)\n",
+ pci_name(hw->pdev), pci_err);
+
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ pci_write_config_word(hw->pdev, PCI_STATUS,
+ pci_err | PCI_STATUS_ERROR_BITS);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+ }
+
+ if (status & Y2_IS_PCI_EXP) {
+ /* PCI-Express uncorrectable Error occurred */
+ u32 pex_err;
+
+ pci_read_config_dword(hw->pdev, PEX_UNC_ERR_STAT, &pex_err);
+
+ printk(KERN_ERR PFX "%s: pci express error (0x%x)\n",
+ pci_name(hw->pdev), pex_err);
+
+ /* clear the interrupt */
+ sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT,
+ 0xffffffffUL);
+ sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+ if (pex_err & PEX_FATAL_ERRORS) {
+ u32 hwmsk = sky2_read32(hw, B0_HWE_IMSK);
+ hwmsk &= ~Y2_IS_PCI_EXP;
+ sky2_write32(hw, B0_HWE_IMSK, hwmsk);
+ }
+ }
+
+ if (status & Y2_HWE_L1_MASK)
+ sky2_hw_error(hw, 0, status);
+ status >>= 8;
+ if (status & Y2_HWE_L1_MASK)
+ sky2_hw_error(hw, 1, status);
+}
+
+static void sky2_mac_intr(struct sky2_hw *hw, unsigned port)
+{
+ struct net_device *dev = hw->dev[port];
+ struct sky2_port *sky2 = netdev_priv(dev);
+ u8 status = sky2_read8(hw, SK_REG(port, GMAC_IRQ_SRC));
+
+ if (netif_msg_intr(sky2))
+ printk(KERN_INFO PFX "%s: mac interrupt status 0x%x\n",
+ dev->name, status);
+
+ if (status & GM_IS_RX_FF_OR) {
+ ++sky2->net_stats.rx_fifo_errors;
+ sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_CLI_RX_FO);
+ }
+
+ if (status & GM_IS_TX_FF_UR) {
+ ++sky2->net_stats.tx_fifo_errors;
+ sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_CLI_TX_FU);
+ }
+}
+
+static void sky2_phy_intr(struct sky2_hw *hw, unsigned port)
+{
+ struct net_device *dev = hw->dev[port];
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2);
+ sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ tasklet_schedule(&sky2->phy_task);
+}
+
+static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct sky2_hw *hw = dev_id;
+ struct net_device *dev0 = hw->dev[0];
+ u32 status;
+
+ status = sky2_read32(hw, B0_Y2_SP_ISRC2);
+ if (status == 0 || status == ~0)
+ return IRQ_NONE;
+
+ if (status & Y2_IS_HW_ERR)
+ sky2_hw_intr(hw);
+
+ /* Do NAPI for Rx and Tx status */
+ if (status & Y2_IS_STAT_BMU) {
+ hw->intr_mask &= ~Y2_IS_STAT_BMU;
+ sky2_write32(hw, B0_IMSK, hw->intr_mask);
+ prefetch(&hw->st_le[hw->st_idx]);
+
+ if (netif_rx_schedule_test(dev0))
+ __netif_rx_schedule(dev0);
+ }
+
+ if (status & Y2_IS_IRQ_PHY1)
+ sky2_phy_intr(hw, 0);
+
+ if (status & Y2_IS_IRQ_PHY2)
+ sky2_phy_intr(hw, 1);
+
+ if (status & Y2_IS_IRQ_MAC1)
+ sky2_mac_intr(hw, 0);
+
+ if (status & Y2_IS_IRQ_MAC2)
+ sky2_mac_intr(hw, 1);
+
+ sky2_write32(hw, B0_Y2_SP_ICR, 2);
+
+ sky2_read32(hw, B0_IMSK);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void sky2_netpoll(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ sky2_intr(sky2->hw->pdev->irq, sky2->hw, NULL);
+}
+#endif
+
+/* Chip internal frequency for clock calculations */
+static inline u32 sky2_khz(const struct sky2_hw *hw)
+{
+ switch (hw->chip_id) {
+ case CHIP_ID_YUKON_EC:
+ return 125000; /* 125 Mhz */
+ case CHIP_ID_YUKON_FE:
+ return 100000; /* 100 Mhz */
+ default: /* YUKON_XL */
+ return 156000; /* 156 Mhz */
+ }
+}
+
+static inline u32 sky2_ms2clk(const struct sky2_hw *hw, u32 ms)
+{
+ return sky2_khz(hw) * ms;
+}
+
+static inline u32 sky2_us2clk(const struct sky2_hw *hw, u32 us)
+{
+ return (sky2_khz(hw) * us) / 1000;
+}
+
+static int sky2_reset(struct sky2_hw *hw)
+{
+ u32 ctst;
+ u16 status;
+ u8 t8, pmd_type;
+ int i;
+
+ ctst = sky2_read32(hw, B0_CTST);
+
+ sky2_write8(hw, B0_CTST, CS_RST_CLR);
+ hw->chip_id = sky2_read8(hw, B2_CHIP_ID);
+ if (hw->chip_id < CHIP_ID_YUKON_XL || hw->chip_id > CHIP_ID_YUKON_FE) {
+ printk(KERN_ERR PFX "%s: unsupported chip type 0x%x\n",
+ pci_name(hw->pdev), hw->chip_id);
+ return -EOPNOTSUPP;
+ }
+
+ /* ring for status responses */
+ hw->st_le = pci_alloc_consistent(hw->pdev, STATUS_LE_BYTES,
+ &hw->st_dma);
+ if (!hw->st_le)
+ return -ENOMEM;
+
+ /* disable ASF */
+ if (hw->chip_id <= CHIP_ID_YUKON_EC) {
+ sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET);
+ sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE);
+ }
+
+ /* do a SW reset */
+ sky2_write8(hw, B0_CTST, CS_RST_SET);
+ sky2_write8(hw, B0_CTST, CS_RST_CLR);
+
+ /* clear PCI errors, if any */
+ pci_read_config_word(hw->pdev, PCI_STATUS, &status);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ pci_write_config_word(hw->pdev, PCI_STATUS,
+ status | PCI_STATUS_ERROR_BITS);
+
+ sky2_write8(hw, B0_CTST, CS_MRST_CLR);
+
+ /* clear any PEX errors */
+ if (is_pciex(hw)) {
+ u16 lstat;
+ pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT,
+ 0xffffffffUL);
+ pci_read_config_word(hw->pdev, PEX_LNK_STAT, &lstat);
+ }
+
+ pmd_type = sky2_read8(hw, B2_PMD_TYP);
+ hw->copper = !(pmd_type == 'L' || pmd_type == 'S');
+
+ hw->ports = 1;
+ t8 = sky2_read8(hw, B2_Y2_HW_RES);
+ if ((t8 & CFG_DUAL_MAC_MSK) == CFG_DUAL_MAC_MSK) {
+ if (!(sky2_read8(hw, B2_Y2_CLK_GATE) & Y2_STATUS_LNK2_INAC))
+ ++hw->ports;
+ }
+ hw->chip_rev = (sky2_read8(hw, B2_MAC_CFG) & CFG_CHIP_R_MSK) >> 4;
+
+ sky2_set_power_state(hw, PCI_D0);
+
+ for (i = 0; i < hw->ports; i++) {
+ sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET);
+ sky2_write8(hw, SK_REG(i, GMAC_LINK_CTRL), GMLC_RST_CLR);
+ }
+
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+ /* Clear I2C IRQ noise */
+ sky2_write32(hw, B2_I2C_IRQ, 1);
+
+ /* turn off hardware timer (unused) */
+ sky2_write8(hw, B2_TI_CTRL, TIM_STOP);
+ sky2_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ);
+
+ sky2_write8(hw, B0_Y2LED, LED_STAT_ON);
+
+ /* Turn on descriptor polling (every 75us) */
+ sky2_write32(hw, B28_DPT_INI, sky2_us2clk(hw, 75));
+ sky2_write8(hw, B28_DPT_CTRL, DPT_START);
+
+ /* Turn off receive timestamp */
+ sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_STOP);
+ sky2_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ);
+
+ /* enable the Tx Arbiters */
+ for (i = 0; i < hw->ports; i++)
+ sky2_write8(hw, SK_REG(i, TXA_CTRL), TXA_ENA_ARB);
+
+ /* Initialize ram interface */
+ for (i = 0; i < hw->ports; i++) {
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_CTRL), RI_RST_CLR);
+
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R1), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA1), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS1), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R1), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA1), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS1), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_R2), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XA2), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_WTO_XS2), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_R2), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XA2), SK_RI_TO_53);
+ sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS2), SK_RI_TO_53);
+ }
+
+ if (is_pciex(hw)) {
+ u16 pctrl;
+
+ /* change Max. Read Request Size to 2048 bytes */
+ pci_read_config_word(hw->pdev, PEX_DEV_CTRL, &pctrl);
+ pctrl &= ~PEX_DC_MAX_RRS_MSK;
+ pctrl |= PEX_DC_MAX_RD_RQ_SIZE(4);
+
+
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ pci_write_config_word(hw->pdev, PEX_DEV_CTRL, pctrl);
+ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+ }
+
+ sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK);
+
+ spin_lock_bh(&hw->phy_lock);
+ for (i = 0; i < hw->ports; i++)
+ sky2_phy_reset(hw, i);
+ spin_unlock_bh(&hw->phy_lock);
+
+ memset(hw->st_le, 0, STATUS_LE_BYTES);
+ hw->st_idx = 0;
+
+ sky2_write32(hw, STAT_CTRL, SC_STAT_RST_SET);
+ sky2_write32(hw, STAT_CTRL, SC_STAT_RST_CLR);
+
+ sky2_write32(hw, STAT_LIST_ADDR_LO, hw->st_dma);
+ sky2_write32(hw, STAT_LIST_ADDR_HI, (u64) hw->st_dma >> 32);
+
+ /* Set the list last index */
+ sky2_write16(hw, STAT_LAST_IDX, STATUS_RING_SIZE - 1);
+
+ sky2_write32(hw, STAT_TX_TIMER_INI, sky2_ms2clk(hw, 10));
+
+ /* These status setup values are copied from SysKonnect's driver */
+ if (is_ec_a1(hw)) {
+ /* WA for dev. #4.3 */
+ sky2_write16(hw, STAT_TX_IDX_TH, 0xfff); /* Tx Threshold */
+
+ /* set Status-FIFO watermark */
+ sky2_write8(hw, STAT_FIFO_WM, 0x21); /* WA for dev. #4.18 */
+
+ /* set Status-FIFO ISR watermark */
+ sky2_write8(hw, STAT_FIFO_ISR_WM, 0x07); /* WA for dev. #4.18 */
+
+ } else {
+ sky2_write16(hw, STAT_TX_IDX_TH, 0x000a);
+
+ /* set Status-FIFO watermark */
+ sky2_write8(hw, STAT_FIFO_WM, 0x10);
+
+ /* set Status-FIFO ISR watermark */
+ if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev == 0)
+ sky2_write8(hw, STAT_FIFO_ISR_WM, 0x10);
+
+ else /* WA dev 4.109 */
+ sky2_write8(hw, STAT_FIFO_ISR_WM, 0x04);
+
+ sky2_write32(hw, STAT_ISR_TIMER_INI, 0x0190);
+ }
+
+ /* enable status unit */
+ sky2_write32(hw, STAT_CTRL, SC_STAT_OP_ON);
+
+ sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START);
+ sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START);
+ sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START);
+
+ return 0;
+}
+
+static inline u32 sky2_supported_modes(const struct sky2_hw *hw)
+{
+ u32 modes;
+ if (hw->copper) {
+ modes = SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_Autoneg | SUPPORTED_TP;
+
+ if (hw->chip_id != CHIP_ID_YUKON_FE)
+ modes |= SUPPORTED_1000baseT_Half
+ | SUPPORTED_1000baseT_Full;
+ } else
+ modes = SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE
+ | SUPPORTED_Autoneg;
+ return modes;
+}
+
+static int sky2_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+
+ ecmd->transceiver = XCVR_INTERNAL;
+ ecmd->supported = sky2_supported_modes(hw);
+ ecmd->phy_address = PHY_ADDR_MARV;
+ if (hw->copper) {
+ ecmd->supported = SUPPORTED_10baseT_Half
+ | SUPPORTED_10baseT_Full
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | SUPPORTED_1000baseT_Half
+ | SUPPORTED_1000baseT_Full
+ | SUPPORTED_Autoneg | SUPPORTED_TP;
+ ecmd->port = PORT_TP;
+ } else
+ ecmd->port = PORT_FIBRE;
+
+ ecmd->advertising = sky2->advertising;
+ ecmd->autoneg = sky2->autoneg;
+ ecmd->speed = sky2->speed;
+ ecmd->duplex = sky2->duplex;
+ return 0;
+}
+
+static int sky2_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ const struct sky2_hw *hw = sky2->hw;
+ u32 supported = sky2_supported_modes(hw);
+
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ ecmd->advertising = supported;
+ sky2->duplex = -1;
+ sky2->speed = -1;
+ } else {
+ u32 setting;
+
+ switch (ecmd->speed) {
+ case SPEED_1000:
+ if (ecmd->duplex == DUPLEX_FULL)
+ setting = SUPPORTED_1000baseT_Full;
+ else if (ecmd->duplex == DUPLEX_HALF)
+ setting = SUPPORTED_1000baseT_Half;
+ else
+ return -EINVAL;
+ break;
+ case SPEED_100:
+ if (ecmd->duplex == DUPLEX_FULL)
+ setting = SUPPORTED_100baseT_Full;
+ else if (ecmd->duplex == DUPLEX_HALF)
+ setting = SUPPORTED_100baseT_Half;
+ else
+ return -EINVAL;
+ break;
+
+ case SPEED_10:
+ if (ecmd->duplex == DUPLEX_FULL)
+ setting = SUPPORTED_10baseT_Full;
+ else if (ecmd->duplex == DUPLEX_HALF)
+ setting = SUPPORTED_10baseT_Half;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((setting & supported) == 0)
+ return -EINVAL;
+
+ sky2->speed = ecmd->speed;
+ sky2->duplex = ecmd->duplex;
+ }
+
+ sky2->autoneg = ecmd->autoneg;
+ sky2->advertising = ecmd->advertising;
+
+ if (netif_running(dev)) {
+ sky2_down(dev);
+ sky2_up(dev);
+ }
+
+ return 0;
+}
+
+static void sky2_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+ strcpy(info->fw_version, "N/A");
+ strcpy(info->bus_info, pci_name(sky2->hw->pdev));
+}
+
+static const struct sky2_stat {
+ char name[ETH_GSTRING_LEN];
+ u16 offset;
+} sky2_stats[] = {
+ { "tx_bytes", GM_TXO_OK_HI },
+ { "rx_bytes", GM_RXO_OK_HI },
+ { "tx_broadcast", GM_TXF_BC_OK },
+ { "rx_broadcast", GM_RXF_BC_OK },
+ { "tx_multicast", GM_TXF_MC_OK },
+ { "rx_multicast", GM_RXF_MC_OK },
+ { "tx_unicast", GM_TXF_UC_OK },
+ { "rx_unicast", GM_RXF_UC_OK },
+ { "tx_mac_pause", GM_TXF_MPAUSE },
+ { "rx_mac_pause", GM_RXF_MPAUSE },
+ { "collisions", GM_TXF_SNG_COL },
+ { "late_collision",GM_TXF_LAT_COL },
+ { "aborted", GM_TXF_ABO_COL },
+ { "multi_collisions", GM_TXF_MUL_COL },
+ { "fifo_underrun", GM_TXE_FIFO_UR },
+ { "fifo_overflow", GM_RXE_FIFO_OV },
+ { "rx_toolong", GM_RXF_LNG_ERR },
+ { "rx_jabber", GM_RXF_JAB_PKT },
+ { "rx_runt", GM_RXE_FRAG },
+ { "rx_too_long", GM_RXF_LNG_ERR },
+ { "rx_fcs_error", GM_RXF_FCS_ERR },
+};
+
+static u32 sky2_get_rx_csum(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ return sky2->rx_csum;
+}
+
+static int sky2_set_rx_csum(struct net_device *dev, u32 data)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ sky2->rx_csum = data;
+
+ sky2_write32(sky2->hw, Q_ADDR(rxqaddr[sky2->port], Q_CSR),
+ data ? BMU_ENA_RX_CHKSUM : BMU_DIS_RX_CHKSUM);
+
+ return 0;
+}
+
+static u32 sky2_get_msglevel(struct net_device *netdev)
+{
+ struct sky2_port *sky2 = netdev_priv(netdev);
+ return sky2->msg_enable;
+}
+
+static int sky2_nway_reset(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+
+ if (sky2->autoneg != AUTONEG_ENABLE)
+ return -EINVAL;
+
+ netif_stop_queue(dev);
+
+ spin_lock_irq(&hw->phy_lock);
+ sky2_phy_reset(hw, sky2->port);
+ sky2_phy_init(hw, sky2->port);
+ spin_unlock_irq(&hw->phy_lock);
+
+ return 0;
+}
+
+static void sky2_phy_stats(struct sky2_port *sky2, u64 * data, unsigned count)
+{
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ int i;
+
+ data[0] = (u64) gma_read32(hw, port, GM_TXO_OK_HI) << 32
+ | (u64) gma_read32(hw, port, GM_TXO_OK_LO);
+ data[1] = (u64) gma_read32(hw, port, GM_RXO_OK_HI) << 32
+ | (u64) gma_read32(hw, port, GM_RXO_OK_LO);
+
+ for (i = 2; i < count; i++)
+ data[i] = (u64) gma_read32(hw, port, sky2_stats[i].offset);
+}
+
+static void sky2_set_msglevel(struct net_device *netdev, u32 value)
+{
+ struct sky2_port *sky2 = netdev_priv(netdev);
+ sky2->msg_enable = value;
+}
+
+static int sky2_get_stats_count(struct net_device *dev)
+{
+ return ARRAY_SIZE(sky2_stats);
+}
+
+static void sky2_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 * data)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ sky2_phy_stats(sky2, data, ARRAY_SIZE(sky2_stats));
+}
+
+static void sky2_get_strings(struct net_device *dev, u32 stringset, u8 * data)
+{
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ARRAY_SIZE(sky2_stats); i++)
+ memcpy(data + i * ETH_GSTRING_LEN,
+ sky2_stats[i].name, ETH_GSTRING_LEN);
+ break;
+ }
+}
+
+/* Use hardware MIB variables for critical path statistics and
+ * transmit feedback not reported at interrupt.
+ * Other errors are accounted for in interrupt handler.
+ */
+static struct net_device_stats *sky2_get_stats(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ u64 data[13];
+
+ sky2_phy_stats(sky2, data, ARRAY_SIZE(data));
+
+ sky2->net_stats.tx_bytes = data[0];
+ sky2->net_stats.rx_bytes = data[1];
+ sky2->net_stats.tx_packets = data[2] + data[4] + data[6];
+ sky2->net_stats.rx_packets = data[3] + data[5] + data[7];
+ sky2->net_stats.multicast = data[5] + data[7];
+ sky2->net_stats.collisions = data[10];
+ sky2->net_stats.tx_aborted_errors = data[12];
+
+ return &sky2->net_stats;
+}
+
+static int sky2_set_mac_address(struct net_device *dev, void *p)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sockaddr *addr = p;
+ int err = 0;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ sky2_down(dev);
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ memcpy_toio(sky2->hw->regs + B2_MAC_1 + sky2->port * 8,
+ dev->dev_addr, ETH_ALEN);
+ memcpy_toio(sky2->hw->regs + B2_MAC_2 + sky2->port * 8,
+ dev->dev_addr, ETH_ALEN);
+ if (dev->flags & IFF_UP)
+ err = sky2_up(dev);
+ return err;
+}
+
+static void sky2_set_multicast(struct net_device *dev)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ struct dev_mc_list *list = dev->mc_list;
+ u16 reg;
+ u8 filter[8];
+
+ memset(filter, 0, sizeof(filter));
+
+ reg = gma_read16(hw, port, GM_RX_CTRL);
+ reg |= GM_RXCR_UCF_ENA;
+
+ if (dev->flags & IFF_PROMISC) /* promiscuous */
+ reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
+ else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > 16) /* all multicast */
+ memset(filter, 0xff, sizeof(filter));
+ else if (dev->mc_count == 0) /* no multicast */
+ reg &= ~GM_RXCR_MCF_ENA;
+ else {
+ int i;
+ reg |= GM_RXCR_MCF_ENA;
+
+ for (i = 0; list && i < dev->mc_count; i++, list = list->next) {
+ u32 bit = ether_crc(ETH_ALEN, list->dmi_addr) & 0x3f;
+ filter[bit / 8] |= 1 << (bit % 8);
+ }
+ }
+
+ gma_write16(hw, port, GM_MC_ADDR_H1,
+ (u16) filter[0] | ((u16) filter[1] << 8));
+ gma_write16(hw, port, GM_MC_ADDR_H2,
+ (u16) filter[2] | ((u16) filter[3] << 8));
+ gma_write16(hw, port, GM_MC_ADDR_H3,
+ (u16) filter[4] | ((u16) filter[5] << 8));
+ gma_write16(hw, port, GM_MC_ADDR_H4,
+ (u16) filter[6] | ((u16) filter[7] << 8));
+
+ gma_write16(hw, port, GM_RX_CTRL, reg);
+}
+
+/* Can have one global because blinking is controlled by
+ * ethtool and that is always under RTNL mutex
+ */
+static inline void sky2_led(struct sky2_hw *hw, unsigned port, int on)
+{
+ u16 pg;
+
+ spin_lock_bh(&hw->phy_lock);
+ switch (hw->chip_id) {
+ case CHIP_ID_YUKON_XL:
+ pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL,
+ on ? (PHY_M_LEDC_LOS_CTRL(1) |
+ PHY_M_LEDC_INIT_CTRL(7) |
+ PHY_M_LEDC_STA1_CTRL(7) |
+ PHY_M_LEDC_STA0_CTRL(7))
+ : 0);
+
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+ break;
+
+ default:
+ gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0);
+ gm_phy_write(hw, port, PHY_MARV_LED_OVER,
+ on ? PHY_M_LED_MO_DUP(MO_LED_ON) |
+ PHY_M_LED_MO_10(MO_LED_ON) |
+ PHY_M_LED_MO_100(MO_LED_ON) |
+ PHY_M_LED_MO_1000(MO_LED_ON) |
+ PHY_M_LED_MO_RX(MO_LED_ON)
+ : PHY_M_LED_MO_DUP(MO_LED_OFF) |
+ PHY_M_LED_MO_10(MO_LED_OFF) |
+ PHY_M_LED_MO_100(MO_LED_OFF) |
+ PHY_M_LED_MO_1000(MO_LED_OFF) |
+ PHY_M_LED_MO_RX(MO_LED_OFF));
+
+ }
+ spin_unlock_bh(&hw->phy_lock);
+}
+
+/* blink LED's for finding board */
+static int sky2_phys_id(struct net_device *dev, u32 data)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+ unsigned port = sky2->port;
+ u16 ledctrl, ledover = 0;
+ long ms;
+ int onoff = 1;
+
+ if (!data || data > (u32) (MAX_SCHEDULE_TIMEOUT / HZ))
+ ms = jiffies_to_msecs(MAX_SCHEDULE_TIMEOUT);
+ else
+ ms = data * 1000;
+
+ /* save initial values */
+ spin_lock_bh(&hw->phy_lock);
+ if (hw->chip_id == CHIP_ID_YUKON_XL) {
+ u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+ ledctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+ } else {
+ ledctrl = gm_phy_read(hw, port, PHY_MARV_LED_CTRL);
+ ledover = gm_phy_read(hw, port, PHY_MARV_LED_OVER);
+ }
+ spin_unlock_bh(&hw->phy_lock);
+
+ while (ms > 0) {
+ sky2_led(hw, port, onoff);
+ onoff = !onoff;
+
+ if (msleep_interruptible(250))
+ break; /* interrupted */
+ ms -= 250;
+ }
+
+ /* resume regularly scheduled programming */
+ spin_lock_bh(&hw->phy_lock);
+ if (hw->chip_id == CHIP_ID_YUKON_XL) {
+ u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR);
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3);
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ledctrl);
+ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg);
+ } else {
+ gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl);
+ gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover);
+ }
+ spin_unlock_bh(&hw->phy_lock);
+
+ return 0;
+}
+
+static void sky2_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *ecmd)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ ecmd->tx_pause = sky2->tx_pause;
+ ecmd->rx_pause = sky2->rx_pause;
+ ecmd->autoneg = sky2->autoneg;
+}
+
+static int sky2_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *ecmd)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ int err = 0;
+
+ sky2->autoneg = ecmd->autoneg;
+ sky2->tx_pause = ecmd->tx_pause != 0;
+ sky2->rx_pause = ecmd->rx_pause != 0;
+
+ if (netif_running(dev)) {
+ sky2_down(dev);
+ err = sky2_up(dev);
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_PM
+static void sky2_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = sky2->wol ? WAKE_MAGIC : 0;
+}
+
+static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ struct sky2_hw *hw = sky2->hw;
+
+ if (wol->wolopts != WAKE_MAGIC && wol->wolopts != 0)
+ return -EOPNOTSUPP;
+
+ sky2->wol = wol->wolopts == WAKE_MAGIC;
+
+ if (sky2->wol) {
+ memcpy_toio(hw->regs + WOL_MAC_ADDR, dev->dev_addr, ETH_ALEN);
+
+ sky2_write16(hw, WOL_CTRL_STAT,
+ WOL_CTL_ENA_PME_ON_MAGIC_PKT |
+ WOL_CTL_ENA_MAGIC_PKT_UNIT);
+ } else
+ sky2_write16(hw, WOL_CTRL_STAT, WOL_CTL_DEFAULT);
+
+ return 0;
+}
+#endif
+
+static void sky2_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ ering->rx_max_pending = RX_MAX_PENDING;
+ ering->rx_mini_max_pending = 0;
+ ering->rx_jumbo_max_pending = 0;
+ ering->tx_max_pending = TX_RING_SIZE - 1;
+
+ ering->rx_pending = sky2->rx_pending;
+ ering->rx_mini_pending = 0;
+ ering->rx_jumbo_pending = 0;
+ ering->tx_pending = sky2->tx_pending;
+}
+
+static int sky2_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ering)
+{
+ struct sky2_port *sky2 = netdev_priv(dev);
+ int err = 0;
+
+ if (ering->rx_pending > RX_MAX_PENDING ||
+ ering->rx_pending < 8 ||
+ ering->tx_pending < MAX_SKB_TX_LE ||
+ ering->tx_pending > TX_RING_SIZE - 1)
+ return -EINVAL;
+
+ if (netif_running(dev))
+ sky2_down(dev);
+
+ sky2->rx_pending = ering->rx_pending;
+ sky2->tx_pending = ering->tx_pending;
+
+ if (netif_running(dev))
+ err = sky2_up(dev);
+
+ return err;
+}
+
+static int sky2_get_regs_len(struct net_device *dev)
+{
+ return 0x4000;
+}
+
+/*
+ * Returns copy of control register region
+ * Note: access to the RAM address register set will cause timeouts.
+ */
+static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+ void *p)
+{
+ const struct sky2_port *sky2 = netdev_priv(dev);
+ const void __iomem *io = sky2->hw->regs;
+
+ BUG_ON(regs->len < B3_RI_WTO_R1);
+ regs->version = 1;
+ memset(p, 0, regs->len);
+
+ memcpy_fromio(p, io, B3_RAM_ADDR);
+
+ memcpy_fromio(p + B3_RI_WTO_R1,
+ io + B3_RI_WTO_R1,
+ regs->len - B3_RI_WTO_R1);
+}
+
+static struct ethtool_ops sky2_ethtool_ops = {
+ .get_settings = sky2_get_settings,
+ .set_settings = sky2_set_settings,
+ .get_drvinfo = sky2_get_drvinfo,
+ .get_msglevel = sky2_get_msglevel,
+ .set_msglevel = sky2_set_msglevel,
+ .nway_reset = sky2_nway_reset,
+ .get_regs_len = sky2_get_regs_len,
+ .get_regs = sky2_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_csum,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+ .get_rx_csum = sky2_get_rx_csum,
+ .set_rx_csum = sky2_set_rx_csum,
+ .get_strings = sky2_get_strings,
+ .get_ringparam = sky2_get_ringparam,
+ .set_ringparam = sky2_set_ringparam,
+ .get_pauseparam = sky2_get_pauseparam,
+ .set_pauseparam = sky2_set_pauseparam,
+#ifdef CONFIG_PM
+ .get_wol = sky2_get_wol,
+ .set_wol = sky2_set_wol,
+#endif
+ .phys_id = sky2_phys_id,
+ .get_stats_count = sky2_get_stats_count,
+ .get_ethtool_stats = sky2_get_ethtool_stats,
+ .get_perm_addr = ethtool_op_get_perm_addr,
+};
+
+/* Initialize network device */
+static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
+ unsigned port, int highmem)
+{
+ struct sky2_port *sky2;
+ struct net_device *dev = alloc_etherdev(sizeof(*sky2));
+
+ if (!dev) {
+ printk(KERN_ERR "sky2 etherdev alloc failed");
+ return NULL;
+ }
+
+ SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &hw->pdev->dev);
+ dev->open = sky2_up;
+ dev->stop = sky2_down;
+ dev->hard_start_xmit = sky2_xmit_frame;
+ dev->get_stats = sky2_get_stats;
+ dev->set_multicast_list = sky2_set_multicast;
+ dev->set_mac_address = sky2_set_mac_address;
+ dev->change_mtu = sky2_change_mtu;
+ SET_ETHTOOL_OPS(dev, &sky2_ethtool_ops);
+ dev->tx_timeout = sky2_tx_timeout;
+ dev->watchdog_timeo = TX_WATCHDOG;
+ if (port == 0)
+ dev->poll = sky2_poll;
+ dev->weight = NAPI_WEIGHT;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ dev->poll_controller = sky2_netpoll;
+#endif
+
+ sky2 = netdev_priv(dev);
+ sky2->netdev = dev;
+ sky2->hw = hw;
+ sky2->msg_enable = netif_msg_init(debug, default_msg);
+
+ spin_lock_init(&sky2->tx_lock);
+ /* Auto speed and flow control */
+ sky2->autoneg = AUTONEG_ENABLE;
+ sky2->tx_pause = 0;
+ sky2->rx_pause = 1;
+ sky2->duplex = -1;
+ sky2->speed = -1;
+ sky2->advertising = sky2_supported_modes(hw);
+ sky2->rx_csum = 1;
+ tasklet_init(&sky2->phy_task, sky2_phy_task, (unsigned long)sky2);
+ sky2->tx_pending = TX_DEF_PENDING;
+ sky2->rx_pending = is_ec_a1(hw) ? 8 : RX_DEF_PENDING;
+
+ hw->dev[port] = dev;
+
+ sky2->port = port;
+
+ dev->features |= NETIF_F_LLTX | NETIF_F_TSO;
+ if (highmem)
+ dev->features |= NETIF_F_HIGHDMA;
+ dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
+
+#ifdef SKY2_VLAN_TAG_USED
+ dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ dev->vlan_rx_register = sky2_vlan_rx_register;
+ dev->vlan_rx_kill_vid = sky2_vlan_rx_kill_vid;
+#endif
+
+ /* read the mac address */
+ memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN);
+ memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
+
+ /* device is off until link detection */
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ return dev;
+}
+
+static inline void sky2_show_addr(struct net_device *dev)
+{
+ const struct sky2_port *sky2 = netdev_priv(dev);
+
+ if (netif_msg_probe(sky2))
+ printk(KERN_INFO PFX "%s: addr %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+}
+
+static int __devinit sky2_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *dev, *dev1 = NULL;
+ struct sky2_hw *hw;
+ int err, pm_cap, using_dac = 0;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR PFX "%s cannot enable PCI device\n",
+ pci_name(pdev));
+ goto err_out;
+ }
+
+ err = pci_request_regions(pdev, DRV_NAME);
+ if (err) {
+ printk(KERN_ERR PFX "%s cannot obtain PCI resources\n",
+ pci_name(pdev));
+ goto err_out;
+ }
+
+ pci_set_master(pdev);
+
+ /* Find power-management capability. */
+ pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pm_cap == 0) {
+ printk(KERN_ERR PFX "Cannot find PowerManagement capability, "
+ "aborting.\n");
+ err = -EIO;
+ goto err_out_free_regions;
+ }
+
+ if (sizeof(dma_addr_t) > sizeof(u32)) {
+ err = pci_set_dma_mask(pdev, DMA_64BIT_MASK);
+ if (!err)
+ using_dac = 1;
+ }
+
+ if (!using_dac) {
+ err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (err) {
+ printk(KERN_ERR PFX "%s no usable DMA configuration\n",
+ pci_name(pdev));
+ goto err_out_free_regions;
+ }
+ }
+#ifdef __BIG_ENDIAN
+ /* byte swap descriptors in hardware */
+ {
+ u32 reg;
+
+ pci_read_config_dword(pdev, PCI_DEV_REG2, ®);
+ reg |= PCI_REV_DESC;
+ pci_write_config_dword(pdev, PCI_DEV_REG2, reg);
+ }
+#endif
+
+ err = -ENOMEM;
+ hw = kmalloc(sizeof(*hw), GFP_KERNEL);
+ if (!hw) {
+ printk(KERN_ERR PFX "%s: cannot allocate hardware struct\n",
+ pci_name(pdev));
+ goto err_out_free_regions;
+ }
+
+ memset(hw, 0, sizeof(*hw));
+ hw->pdev = pdev;
+ spin_lock_init(&hw->phy_lock);
+
+ hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000);
+ if (!hw->regs) {
+ printk(KERN_ERR PFX "%s: cannot map device registers\n",
+ pci_name(pdev));
+ goto err_out_free_hw;
+ }
+ hw->pm_cap = pm_cap;
+
+ err = sky2_reset(hw);
+ if (err)
+ goto err_out_iounmap;
+
+ printk(KERN_INFO PFX "addr 0x%lx irq %d Yukon-%s (0x%x) rev %d\n",
+ pci_resource_start(pdev, 0), pdev->irq,
+ yukon_name[hw->chip_id - CHIP_ID_YUKON],
+ hw->chip_id, hw->chip_rev);
+
+ dev = sky2_init_netdev(hw, 0, using_dac);
+ if (!dev)
+ goto err_out_free_pci;
+
+ err = register_netdev(dev);
+ if (err) {
+ printk(KERN_ERR PFX "%s: cannot register net device\n",
+ pci_name(pdev));
+ goto err_out_free_netdev;
+ }
+
+ sky2_show_addr(dev);
+
+ if (hw->ports > 1 && (dev1 = sky2_init_netdev(hw, 1, using_dac))) {
+ if (register_netdev(dev1) == 0)
+ sky2_show_addr(dev1);
+ else {
+ /* Failure to register second port need not be fatal */
+ printk(KERN_WARNING PFX
+ "register of second port failed\n");
+ hw->dev[1] = NULL;
+ free_netdev(dev1);
+ }
+ }
+
+ err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ, DRV_NAME, hw);
+ if (err) {
+ printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
+ pci_name(pdev), pdev->irq);
+ goto err_out_unregister;
+ }
+
+ hw->intr_mask = Y2_IS_BASE;
+ sky2_write32(hw, B0_IMSK, hw->intr_mask);
+
+ pci_set_drvdata(pdev, hw);
+
+ return 0;
+
+err_out_unregister:
+ if (dev1) {
+ unregister_netdev(dev1);
+ free_netdev(dev1);
+ }
+ unregister_netdev(dev);
+err_out_free_netdev:
+ free_netdev(dev);
+err_out_free_pci:
+ sky2_write8(hw, B0_CTST, CS_RST_SET);
+ pci_free_consistent(hw->pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma);
+err_out_iounmap:
+ iounmap(hw->regs);
+err_out_free_hw:
+ kfree(hw);
+err_out_free_regions:
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+err_out:
+ return err;
+}
+
+static void __devexit sky2_remove(struct pci_dev *pdev)
+{
+ struct sky2_hw *hw = pci_get_drvdata(pdev);
+ struct net_device *dev0, *dev1;
+
+ if (!hw)
+ return;
+
+ dev0 = hw->dev[0];
+ dev1 = hw->dev[1];
+ if (dev1)
+ unregister_netdev(dev1);
+ unregister_netdev(dev0);
+
+ sky2_write32(hw, B0_IMSK, 0);
+ sky2_set_power_state(hw, PCI_D3hot);
+ sky2_write16(hw, B0_Y2LED, LED_STAT_OFF);
+ sky2_write8(hw, B0_CTST, CS_RST_SET);
+ sky2_read8(hw, B0_CTST);
+
+ free_irq(pdev->irq, hw);
+ pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+
+ if (dev1)
+ free_netdev(dev1);
+ free_netdev(dev0);
+ iounmap(hw->regs);
+ kfree(hw);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int sky2_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct sky2_hw *hw = pci_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ struct net_device *dev = hw->dev[i];
+
+ if (dev) {
+ if (!netif_running(dev))
+ continue;
+
+ sky2_down(dev);
+ netif_device_detach(dev);
+ }
+ }
+
+ return sky2_set_power_state(hw, pci_choose_state(pdev, state));
+}
+
+static int sky2_resume(struct pci_dev *pdev)
+{
+ struct sky2_hw *hw = pci_get_drvdata(pdev);
+ int i;
+
+ pci_restore_state(pdev);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ sky2_set_power_state(hw, PCI_D0);
+
+ sky2_reset(hw);
+
+ for (i = 0; i < 2; i++) {
+ struct net_device *dev = hw->dev[i];
+ if (dev) {
+ if (netif_running(dev)) {
+ netif_device_attach(dev);
+ sky2_up(dev);
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+static struct pci_driver sky2_driver = {
+ .name = DRV_NAME,
+ .id_table = sky2_id_table,
+ .probe = sky2_probe,
+ .remove = __devexit_p(sky2_remove),
+#ifdef CONFIG_PM
+ .suspend = sky2_suspend,
+ .resume = sky2_resume,
+#endif
+};
+
+static int __init sky2_init_module(void)
+{
+ return pci_module_init(&sky2_driver);
+}
+
+static void __exit sky2_cleanup_module(void)
+{
+ pci_unregister_driver(&sky2_driver);
+}
+
+module_init(sky2_init_module);
+module_exit(sky2_cleanup_module);
+
+MODULE_DESCRIPTION("Marvell Yukon 2 Gigabit Ethernet driver");
+MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Definitions for the new Marvell Yukon 2 driver.
+ */
+#ifndef _SKY2_H
+#define _SKY2_H
+
+/* PCI config registers */
+#define PCI_DEV_REG1 0x40
+#define PCI_DEV_REG2 0x44
+#define PCI_DEV_STATUS 0x7c
+#define PCI_OS_PCI_X (1<<26)
+
+#define PEX_LNK_STAT 0xf2
+#define PEX_UNC_ERR_STAT 0x104
+#define PEX_DEV_CTRL 0xe8
+
+/* Yukon-2 */
+enum pci_dev_reg_1 {
+ PCI_Y2_PIG_ENA = 1<<31, /* Enable Plug-in-Go (YUKON-2) */
+ PCI_Y2_DLL_DIS = 1<<30, /* Disable PCI DLL (YUKON-2) */
+ PCI_Y2_PHY2_COMA = 1<<29, /* Set PHY 2 to Coma Mode (YUKON-2) */
+ PCI_Y2_PHY1_COMA = 1<<28, /* Set PHY 1 to Coma Mode (YUKON-2) */
+ PCI_Y2_PHY2_POWD = 1<<27, /* Set PHY 2 to Power Down (YUKON-2) */
+ PCI_Y2_PHY1_POWD = 1<<26, /* Set PHY 1 to Power Down (YUKON-2) */
+};
+
+enum pci_dev_reg_2 {
+ PCI_VPD_WR_THR = 0xffL<<24, /* Bit 31..24: VPD Write Threshold */
+ PCI_DEV_SEL = 0x7fL<<17, /* Bit 23..17: EEPROM Device Select */
+ PCI_VPD_ROM_SZ = 7L<<14, /* Bit 16..14: VPD ROM Size */
+
+ PCI_PATCH_DIR = 0xfL<<8, /* Bit 11.. 8: Ext Patches dir 3..0 */
+ PCI_EXT_PATCHS = 0xfL<<4, /* Bit 7.. 4: Extended Patches 3..0 */
+ PCI_EN_DUMMY_RD = 1<<3, /* Enable Dummy Read */
+ PCI_REV_DESC = 1<<2, /* Reverse Desc. Bytes */
+
+ PCI_USEDATA64 = 1<<0, /* Use 64Bit Data bus ext */
+};
+
+
+#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \
+ PCI_STATUS_SIG_SYSTEM_ERROR | \
+ PCI_STATUS_REC_MASTER_ABORT | \
+ PCI_STATUS_REC_TARGET_ABORT | \
+ PCI_STATUS_PARITY)
+
+enum pex_dev_ctrl {
+ PEX_DC_MAX_RRS_MSK = 7<<12, /* Bit 14..12: Max. Read Request Size */
+ PEX_DC_EN_NO_SNOOP = 1<<11,/* Enable No Snoop */
+ PEX_DC_EN_AUX_POW = 1<<10,/* Enable AUX Power */
+ PEX_DC_EN_PHANTOM = 1<<9, /* Enable Phantom Functions */
+ PEX_DC_EN_EXT_TAG = 1<<8, /* Enable Extended Tag Field */
+ PEX_DC_MAX_PLS_MSK = 7<<5, /* Bit 7.. 5: Max. Payload Size Mask */
+ PEX_DC_EN_REL_ORD = 1<<4, /* Enable Relaxed Ordering */
+ PEX_DC_EN_UNS_RQ_RP = 1<<3, /* Enable Unsupported Request Reporting */
+ PEX_DC_EN_FAT_ER_RP = 1<<2, /* Enable Fatal Error Reporting */
+ PEX_DC_EN_NFA_ER_RP = 1<<1, /* Enable Non-Fatal Error Reporting */
+ PEX_DC_EN_COR_ER_RP = 1<<0, /* Enable Correctable Error Reporting */
+};
+#define PEX_DC_MAX_RD_RQ_SIZE(x) (((x)<<12) & PEX_DC_MAX_RRS_MSK)
+
+/* PEX_UNC_ERR_STAT PEX Uncorrectable Errors Status Register (Yukon-2) */
+enum pex_err {
+ PEX_UNSUP_REQ = 1<<20, /* Unsupported Request Error */
+
+ PEX_MALFOR_TLP = 1<<18, /* Malformed TLP */
+
+ PEX_UNEXP_COMP = 1<<16, /* Unexpected Completion */
+
+ PEX_COMP_TO = 1<<14, /* Completion Timeout */
+ PEX_FLOW_CTRL_P = 1<<13, /* Flow Control Protocol Error */
+ PEX_POIS_TLP = 1<<12, /* Poisoned TLP */
+
+ PEX_DATA_LINK_P = 1<<4, /* Data Link Protocol Error */
+ PEX_FATAL_ERRORS= (PEX_MALFOR_TLP | PEX_FLOW_CTRL_P | PEX_DATA_LINK_P),
+};
+
+
+enum csr_regs {
+ B0_RAP = 0x0000,
+ B0_CTST = 0x0004,
+ B0_Y2LED = 0x0005,
+ B0_POWER_CTRL = 0x0007,
+ B0_ISRC = 0x0008,
+ B0_IMSK = 0x000c,
+ B0_HWE_ISRC = 0x0010,
+ B0_HWE_IMSK = 0x0014,
+
+ /* Special ISR registers (Yukon-2 only) */
+ B0_Y2_SP_ISRC2 = 0x001c,
+ B0_Y2_SP_ISRC3 = 0x0020,
+ B0_Y2_SP_EISR = 0x0024,
+ B0_Y2_SP_LISR = 0x0028,
+ B0_Y2_SP_ICR = 0x002c,
+
+ B2_MAC_1 = 0x0100,
+ B2_MAC_2 = 0x0108,
+ B2_MAC_3 = 0x0110,
+ B2_CONN_TYP = 0x0118,
+ B2_PMD_TYP = 0x0119,
+ B2_MAC_CFG = 0x011a,
+ B2_CHIP_ID = 0x011b,
+ B2_E_0 = 0x011c,
+
+ B2_Y2_CLK_GATE = 0x011d,
+ B2_Y2_HW_RES = 0x011e,
+ B2_E_3 = 0x011f,
+ B2_Y2_CLK_CTRL = 0x0120,
+
+ B2_TI_INI = 0x0130,
+ B2_TI_VAL = 0x0134,
+ B2_TI_CTRL = 0x0138,
+ B2_TI_TEST = 0x0139,
+
+ B2_TST_CTRL1 = 0x0158,
+ B2_TST_CTRL2 = 0x0159,
+ B2_GP_IO = 0x015c,
+
+ B2_I2C_CTRL = 0x0160,
+ B2_I2C_DATA = 0x0164,
+ B2_I2C_IRQ = 0x0168,
+ B2_I2C_SW = 0x016c,
+
+ B3_RAM_ADDR = 0x0180,
+ B3_RAM_DATA_LO = 0x0184,
+ B3_RAM_DATA_HI = 0x0188,
+
+/* RAM Interface Registers */
+/* Yukon-2: use RAM_BUFFER() to access the RAM buffer */
+/*
+ * The HW-Spec. calls this registers Timeout Value 0..11. But this names are
+ * not usable in SW. Please notice these are NOT real timeouts, these are
+ * the number of qWords transferred continuously.
+ */
+#define RAM_BUFFER(port, reg) (reg | (port <<6))
+
+ B3_RI_WTO_R1 = 0x0190,
+ B3_RI_WTO_XA1 = 0x0191,
+ B3_RI_WTO_XS1 = 0x0192,
+ B3_RI_RTO_R1 = 0x0193,
+ B3_RI_RTO_XA1 = 0x0194,
+ B3_RI_RTO_XS1 = 0x0195,
+ B3_RI_WTO_R2 = 0x0196,
+ B3_RI_WTO_XA2 = 0x0197,
+ B3_RI_WTO_XS2 = 0x0198,
+ B3_RI_RTO_R2 = 0x0199,
+ B3_RI_RTO_XA2 = 0x019a,
+ B3_RI_RTO_XS2 = 0x019b,
+ B3_RI_TO_VAL = 0x019c,
+ B3_RI_CTRL = 0x01a0,
+ B3_RI_TEST = 0x01a2,
+ B3_MA_TOINI_RX1 = 0x01b0,
+ B3_MA_TOINI_RX2 = 0x01b1,
+ B3_MA_TOINI_TX1 = 0x01b2,
+ B3_MA_TOINI_TX2 = 0x01b3,
+ B3_MA_TOVAL_RX1 = 0x01b4,
+ B3_MA_TOVAL_RX2 = 0x01b5,
+ B3_MA_TOVAL_TX1 = 0x01b6,
+ B3_MA_TOVAL_TX2 = 0x01b7,
+ B3_MA_TO_CTRL = 0x01b8,
+ B3_MA_TO_TEST = 0x01ba,
+ B3_MA_RCINI_RX1 = 0x01c0,
+ B3_MA_RCINI_RX2 = 0x01c1,
+ B3_MA_RCINI_TX1 = 0x01c2,
+ B3_MA_RCINI_TX2 = 0x01c3,
+ B3_MA_RCVAL_RX1 = 0x01c4,
+ B3_MA_RCVAL_RX2 = 0x01c5,
+ B3_MA_RCVAL_TX1 = 0x01c6,
+ B3_MA_RCVAL_TX2 = 0x01c7,
+ B3_MA_RC_CTRL = 0x01c8,
+ B3_MA_RC_TEST = 0x01ca,
+ B3_PA_TOINI_RX1 = 0x01d0,
+ B3_PA_TOINI_RX2 = 0x01d4,
+ B3_PA_TOINI_TX1 = 0x01d8,
+ B3_PA_TOINI_TX2 = 0x01dc,
+ B3_PA_TOVAL_RX1 = 0x01e0,
+ B3_PA_TOVAL_RX2 = 0x01e4,
+ B3_PA_TOVAL_TX1 = 0x01e8,
+ B3_PA_TOVAL_TX2 = 0x01ec,
+ B3_PA_CTRL = 0x01f0,
+ B3_PA_TEST = 0x01f2,
+
+ Y2_CFG_SPC = 0x1c00,
+};
+
+/* B0_CTST 16 bit Control/Status register */
+enum {
+ Y2_VMAIN_AVAIL = 1<<17,/* VMAIN available (YUKON-2 only) */
+ Y2_VAUX_AVAIL = 1<<16,/* VAUX available (YUKON-2 only) */
+ Y2_ASF_ENABLE = 1<<13,/* ASF Unit Enable (YUKON-2 only) */
+ Y2_ASF_DISABLE = 1<<12,/* ASF Unit Disable (YUKON-2 only) */
+ Y2_CLK_RUN_ENA = 1<<11,/* CLK_RUN Enable (YUKON-2 only) */
+ Y2_CLK_RUN_DIS = 1<<10,/* CLK_RUN Disable (YUKON-2 only) */
+ Y2_LED_STAT_ON = 1<<9, /* Status LED On (YUKON-2 only) */
+ Y2_LED_STAT_OFF = 1<<8, /* Status LED Off (YUKON-2 only) */
+
+ CS_ST_SW_IRQ = 1<<7, /* Set IRQ SW Request */
+ CS_CL_SW_IRQ = 1<<6, /* Clear IRQ SW Request */
+ CS_STOP_DONE = 1<<5, /* Stop Master is finished */
+ CS_STOP_MAST = 1<<4, /* Command Bit to stop the master */
+ CS_MRST_CLR = 1<<3, /* Clear Master reset */
+ CS_MRST_SET = 1<<2, /* Set Master reset */
+ CS_RST_CLR = 1<<1, /* Clear Software reset */
+ CS_RST_SET = 1, /* Set Software reset */
+};
+
+/* B0_LED 8 Bit LED register */
+enum {
+/* Bit 7.. 2: reserved */
+ LED_STAT_ON = 1<<1, /* Status LED on */
+ LED_STAT_OFF = 1, /* Status LED off */
+};
+
+/* B0_POWER_CTRL 8 Bit Power Control reg (YUKON only) */
+enum {
+ PC_VAUX_ENA = 1<<7, /* Switch VAUX Enable */
+ PC_VAUX_DIS = 1<<6, /* Switch VAUX Disable */
+ PC_VCC_ENA = 1<<5, /* Switch VCC Enable */
+ PC_VCC_DIS = 1<<4, /* Switch VCC Disable */
+ PC_VAUX_ON = 1<<3, /* Switch VAUX On */
+ PC_VAUX_OFF = 1<<2, /* Switch VAUX Off */
+ PC_VCC_ON = 1<<1, /* Switch VCC On */
+ PC_VCC_OFF = 1<<0, /* Switch VCC Off */
+};
+
+/* B2_IRQM_MSK 32 bit IRQ Moderation Mask */
+
+/* B0_Y2_SP_ISRC2 32 bit Special Interrupt Source Reg 2 */
+/* B0_Y2_SP_ISRC3 32 bit Special Interrupt Source Reg 3 */
+/* B0_Y2_SP_EISR 32 bit Enter ISR Reg */
+/* B0_Y2_SP_LISR 32 bit Leave ISR Reg */
+enum {
+ Y2_IS_HW_ERR = 1<<31, /* Interrupt HW Error */
+ Y2_IS_STAT_BMU = 1<<30, /* Status BMU Interrupt */
+ Y2_IS_ASF = 1<<29, /* ASF subsystem Interrupt */
+
+ Y2_IS_POLL_CHK = 1<<27, /* Check IRQ from polling unit */
+ Y2_IS_TWSI_RDY = 1<<26, /* IRQ on end of TWSI Tx */
+ Y2_IS_IRQ_SW = 1<<25, /* SW forced IRQ */
+ Y2_IS_TIMINT = 1<<24, /* IRQ from Timer */
+
+ Y2_IS_IRQ_PHY2 = 1<<12, /* Interrupt from PHY 2 */
+ Y2_IS_IRQ_MAC2 = 1<<11, /* Interrupt from MAC 2 */
+ Y2_IS_CHK_RX2 = 1<<10, /* Descriptor error Rx 2 */
+ Y2_IS_CHK_TXS2 = 1<<9, /* Descriptor error TXS 2 */
+ Y2_IS_CHK_TXA2 = 1<<8, /* Descriptor error TXA 2 */
+
+ Y2_IS_IRQ_PHY1 = 1<<4, /* Interrupt from PHY 1 */
+ Y2_IS_IRQ_MAC1 = 1<<3, /* Interrupt from MAC 1 */
+ Y2_IS_CHK_RX1 = 1<<2, /* Descriptor error Rx 1 */
+ Y2_IS_CHK_TXS1 = 1<<1, /* Descriptor error TXS 1 */
+ Y2_IS_CHK_TXA1 = 1<<0, /* Descriptor error TXA 1 */
+
+ Y2_IS_BASE = Y2_IS_HW_ERR | Y2_IS_STAT_BMU |
+ Y2_IS_POLL_CHK | Y2_IS_TWSI_RDY |
+ Y2_IS_IRQ_SW | Y2_IS_TIMINT,
+ Y2_IS_PORT_1 = Y2_IS_IRQ_PHY1 | Y2_IS_IRQ_MAC1 |
+ Y2_IS_CHK_RX1 | Y2_IS_CHK_TXA1 | Y2_IS_CHK_TXS1,
+ Y2_IS_PORT_2 = Y2_IS_IRQ_PHY2 | Y2_IS_IRQ_MAC2 |
+ Y2_IS_CHK_RX2 | Y2_IS_CHK_TXA2 | Y2_IS_CHK_TXS2,
+};
+
+/* B2_IRQM_HWE_MSK 32 bit IRQ Moderation HW Error Mask */
+enum {
+ IS_ERR_MSK = 0x00003fff,/* All Error bits */
+
+ IS_IRQ_TIST_OV = 1<<13, /* Time Stamp Timer Overflow (YUKON only) */
+ IS_IRQ_SENSOR = 1<<12, /* IRQ from Sensor (YUKON only) */
+ IS_IRQ_MST_ERR = 1<<11, /* IRQ master error detected */
+ IS_IRQ_STAT = 1<<10, /* IRQ status exception */
+ IS_NO_STAT_M1 = 1<<9, /* No Rx Status from MAC 1 */
+ IS_NO_STAT_M2 = 1<<8, /* No Rx Status from MAC 2 */
+ IS_NO_TIST_M1 = 1<<7, /* No Time Stamp from MAC 1 */
+ IS_NO_TIST_M2 = 1<<6, /* No Time Stamp from MAC 2 */
+ IS_RAM_RD_PAR = 1<<5, /* RAM Read Parity Error */
+ IS_RAM_WR_PAR = 1<<4, /* RAM Write Parity Error */
+ IS_M1_PAR_ERR = 1<<3, /* MAC 1 Parity Error */
+ IS_M2_PAR_ERR = 1<<2, /* MAC 2 Parity Error */
+ IS_R1_PAR_ERR = 1<<1, /* Queue R1 Parity Error */
+ IS_R2_PAR_ERR = 1<<0, /* Queue R2 Parity Error */
+};
+
+/* Hardware error interrupt mask for Yukon 2 */
+enum {
+ Y2_IS_TIST_OV = 1<<29,/* Time Stamp Timer overflow interrupt */
+ Y2_IS_SENSOR = 1<<28, /* Sensor interrupt */
+ Y2_IS_MST_ERR = 1<<27, /* Master error interrupt */
+ Y2_IS_IRQ_STAT = 1<<26, /* Status exception interrupt */
+ Y2_IS_PCI_EXP = 1<<25, /* PCI-Express interrupt */
+ Y2_IS_PCI_NEXP = 1<<24, /* PCI-Express error similar to PCI error */
+ /* Link 2 */
+ Y2_IS_PAR_RD2 = 1<<13, /* Read RAM parity error interrupt */
+ Y2_IS_PAR_WR2 = 1<<12, /* Write RAM parity error interrupt */
+ Y2_IS_PAR_MAC2 = 1<<11, /* MAC hardware fault interrupt */
+ Y2_IS_PAR_RX2 = 1<<10, /* Parity Error Rx Queue 2 */
+ Y2_IS_TCP_TXS2 = 1<<9, /* TCP length mismatch sync Tx queue IRQ */
+ Y2_IS_TCP_TXA2 = 1<<8, /* TCP length mismatch async Tx queue IRQ */
+ /* Link 1 */
+ Y2_IS_PAR_RD1 = 1<<5, /* Read RAM parity error interrupt */
+ Y2_IS_PAR_WR1 = 1<<4, /* Write RAM parity error interrupt */
+ Y2_IS_PAR_MAC1 = 1<<3, /* MAC hardware fault interrupt */
+ Y2_IS_PAR_RX1 = 1<<2, /* Parity Error Rx Queue 1 */
+ Y2_IS_TCP_TXS1 = 1<<1, /* TCP length mismatch sync Tx queue IRQ */
+ Y2_IS_TCP_TXA1 = 1<<0, /* TCP length mismatch async Tx queue IRQ */
+
+ Y2_HWE_L1_MASK = Y2_IS_PAR_RD1 | Y2_IS_PAR_WR1 | Y2_IS_PAR_MAC1 |
+ Y2_IS_PAR_RX1 | Y2_IS_TCP_TXS1| Y2_IS_TCP_TXA1,
+ Y2_HWE_L2_MASK = Y2_IS_PAR_RD2 | Y2_IS_PAR_WR2 | Y2_IS_PAR_MAC2 |
+ Y2_IS_PAR_RX2 | Y2_IS_TCP_TXS2| Y2_IS_TCP_TXA2,
+
+ Y2_HWE_ALL_MASK = Y2_IS_TIST_OV | Y2_IS_MST_ERR | Y2_IS_IRQ_STAT |
+ Y2_IS_PCI_EXP | Y2_IS_PCI_NEXP |
+ Y2_HWE_L1_MASK | Y2_HWE_L2_MASK,
+};
+
+/* B28_DPT_CTRL 8 bit Descriptor Poll Timer Ctrl Reg */
+enum {
+ DPT_START = 1<<1,
+ DPT_STOP = 1<<0,
+};
+
+/* B2_TST_CTRL1 8 bit Test Control Register 1 */
+enum {
+ TST_FRC_DPERR_MR = 1<<7, /* force DATAPERR on MST RD */
+ TST_FRC_DPERR_MW = 1<<6, /* force DATAPERR on MST WR */
+ TST_FRC_DPERR_TR = 1<<5, /* force DATAPERR on TRG RD */
+ TST_FRC_DPERR_TW = 1<<4, /* force DATAPERR on TRG WR */
+ TST_FRC_APERR_M = 1<<3, /* force ADDRPERR on MST */
+ TST_FRC_APERR_T = 1<<2, /* force ADDRPERR on TRG */
+ TST_CFG_WRITE_ON = 1<<1, /* Enable Config Reg WR */
+ TST_CFG_WRITE_OFF= 1<<0, /* Disable Config Reg WR */
+};
+
+/* B2_MAC_CFG 8 bit MAC Configuration / Chip Revision */
+enum {
+ CFG_CHIP_R_MSK = 0xf<<4, /* Bit 7.. 4: Chip Revision */
+ /* Bit 3.. 2: reserved */
+ CFG_DIS_M2_CLK = 1<<1, /* Disable Clock for 2nd MAC */
+ CFG_SNG_MAC = 1<<0, /* MAC Config: 0=2 MACs / 1=1 MAC*/
+};
+
+/* B2_CHIP_ID 8 bit Chip Identification Number */
+enum {
+ CHIP_ID_GENESIS = 0x0a, /* Chip ID for GENESIS */
+ CHIP_ID_YUKON = 0xb0, /* Chip ID for YUKON */
+ CHIP_ID_YUKON_LITE = 0xb1, /* Chip ID for YUKON-Lite (Rev. A1-A3) */
+ CHIP_ID_YUKON_LP = 0xb2, /* Chip ID for YUKON-LP */
+ CHIP_ID_YUKON_XL = 0xb3, /* Chip ID for YUKON-2 XL */
+ CHIP_ID_YUKON_EC = 0xb6, /* Chip ID for YUKON-2 EC */
+ CHIP_ID_YUKON_FE = 0xb7, /* Chip ID for YUKON-2 FE */
+
+ CHIP_REV_YU_EC_A1 = 0, /* Chip Rev. for Yukon-EC A1/A0 */
+ CHIP_REV_YU_EC_A2 = 1, /* Chip Rev. for Yukon-EC A2 */
+ CHIP_REV_YU_EC_A3 = 2, /* Chip Rev. for Yukon-EC A3 */
+};
+
+/* B2_Y2_CLK_GATE 8 bit Clock Gating (Yukon-2 only) */
+enum {
+ Y2_STATUS_LNK2_INAC = 1<<7, /* Status Link 2 inactive (0 = active) */
+ Y2_CLK_GAT_LNK2_DIS = 1<<6, /* Disable clock gating Link 2 */
+ Y2_COR_CLK_LNK2_DIS = 1<<5, /* Disable Core clock Link 2 */
+ Y2_PCI_CLK_LNK2_DIS = 1<<4, /* Disable PCI clock Link 2 */
+ Y2_STATUS_LNK1_INAC = 1<<3, /* Status Link 1 inactive (0 = active) */
+ Y2_CLK_GAT_LNK1_DIS = 1<<2, /* Disable clock gating Link 1 */
+ Y2_COR_CLK_LNK1_DIS = 1<<1, /* Disable Core clock Link 1 */
+ Y2_PCI_CLK_LNK1_DIS = 1<<0, /* Disable PCI clock Link 1 */
+};
+
+/* B2_Y2_HW_RES 8 bit HW Resources (Yukon-2 only) */
+enum {
+ CFG_LED_MODE_MSK = 7<<2, /* Bit 4.. 2: LED Mode Mask */
+ CFG_LINK_2_AVAIL = 1<<1, /* Link 2 available */
+ CFG_LINK_1_AVAIL = 1<<0, /* Link 1 available */
+};
+#define CFG_LED_MODE(x) (((x) & CFG_LED_MODE_MSK) >> 2)
+#define CFG_DUAL_MAC_MSK (CFG_LINK_2_AVAIL | CFG_LINK_1_AVAIL)
+
+
+/* B2_Y2_CLK_CTRL 32 bit Clock Frequency Control Register (Yukon-2/EC) */
+enum {
+ Y2_CLK_DIV_VAL_MSK = 0xff<<16,/* Bit 23..16: Clock Divisor Value */
+#define Y2_CLK_DIV_VAL(x) (((x)<<16) & Y2_CLK_DIV_VAL_MSK)
+ Y2_CLK_DIV_VAL2_MSK = 7<<21, /* Bit 23..21: Clock Divisor Value */
+ Y2_CLK_SELECT2_MSK = 0x1f<<16,/* Bit 20..16: Clock Select */
+#define Y2_CLK_DIV_VAL_2(x) (((x)<<21) & Y2_CLK_DIV_VAL2_MSK)
+#define Y2_CLK_SEL_VAL_2(x) (((x)<<16) & Y2_CLK_SELECT2_MSK)
+ Y2_CLK_DIV_ENA = 1<<1, /* Enable Core Clock Division */
+ Y2_CLK_DIV_DIS = 1<<0, /* Disable Core Clock Division */
+};
+
+/* B2_TI_CTRL 8 bit Timer control */
+/* B2_IRQM_CTRL 8 bit IRQ Moderation Timer Control */
+enum {
+ TIM_START = 1<<2, /* Start Timer */
+ TIM_STOP = 1<<1, /* Stop Timer */
+ TIM_CLR_IRQ = 1<<0, /* Clear Timer IRQ (!IRQM) */
+};
+
+/* B2_TI_TEST 8 Bit Timer Test */
+/* B2_IRQM_TEST 8 bit IRQ Moderation Timer Test */
+/* B28_DPT_TST 8 bit Descriptor Poll Timer Test Reg */
+enum {
+ TIM_T_ON = 1<<2, /* Test mode on */
+ TIM_T_OFF = 1<<1, /* Test mode off */
+ TIM_T_STEP = 1<<0, /* Test step */
+};
+
+/* B3_RAM_ADDR 32 bit RAM Address, to read or write */
+ /* Bit 31..19: reserved */
+#define RAM_ADR_RAN 0x0007ffffL /* Bit 18.. 0: RAM Address Range */
+/* RAM Interface Registers */
+
+/* B3_RI_CTRL 16 bit RAM Interface Control Register */
+enum {
+ RI_CLR_RD_PERR = 1<<9, /* Clear IRQ RAM Read Parity Err */
+ RI_CLR_WR_PERR = 1<<8, /* Clear IRQ RAM Write Parity Err*/
+
+ RI_RST_CLR = 1<<1, /* Clear RAM Interface Reset */
+ RI_RST_SET = 1<<0, /* Set RAM Interface Reset */
+};
+
+#define SK_RI_TO_53 36 /* RAM interface timeout */
+
+
+/* Port related registers FIFO, and Arbiter */
+#define SK_REG(port,reg) (((port)<<7)+(reg))
+
+/* Transmit Arbiter Registers MAC 1 and 2, use SK_REG() to access */
+/* TXA_ITI_INI 32 bit Tx Arb Interval Timer Init Val */
+/* TXA_ITI_VAL 32 bit Tx Arb Interval Timer Value */
+/* TXA_LIM_INI 32 bit Tx Arb Limit Counter Init Val */
+/* TXA_LIM_VAL 32 bit Tx Arb Limit Counter Value */
+
+#define TXA_MAX_VAL 0x00ffffffUL /* Bit 23.. 0: Max TXA Timer/Cnt Val */
+
+/* TXA_CTRL 8 bit Tx Arbiter Control Register */
+enum {
+ TXA_ENA_FSYNC = 1<<7, /* Enable force of sync Tx queue */
+ TXA_DIS_FSYNC = 1<<6, /* Disable force of sync Tx queue */
+ TXA_ENA_ALLOC = 1<<5, /* Enable alloc of free bandwidth */
+ TXA_DIS_ALLOC = 1<<4, /* Disable alloc of free bandwidth */
+ TXA_START_RC = 1<<3, /* Start sync Rate Control */
+ TXA_STOP_RC = 1<<2, /* Stop sync Rate Control */
+ TXA_ENA_ARB = 1<<1, /* Enable Tx Arbiter */
+ TXA_DIS_ARB = 1<<0, /* Disable Tx Arbiter */
+};
+
+/*
+ * Bank 4 - 5
+ */
+/* Transmit Arbiter Registers MAC 1 and 2, use SK_REG() to access */
+enum {
+ TXA_ITI_INI = 0x0200,/* 32 bit Tx Arb Interval Timer Init Val*/
+ TXA_ITI_VAL = 0x0204,/* 32 bit Tx Arb Interval Timer Value */
+ TXA_LIM_INI = 0x0208,/* 32 bit Tx Arb Limit Counter Init Val */
+ TXA_LIM_VAL = 0x020c,/* 32 bit Tx Arb Limit Counter Value */
+ TXA_CTRL = 0x0210,/* 8 bit Tx Arbiter Control Register */
+ TXA_TEST = 0x0211,/* 8 bit Tx Arbiter Test Register */
+ TXA_STAT = 0x0212,/* 8 bit Tx Arbiter Status Register */
+};
+
+
+enum {
+ B6_EXT_REG = 0x0300,/* External registers (GENESIS only) */
+ B7_CFG_SPC = 0x0380,/* copy of the Configuration register */
+ B8_RQ1_REGS = 0x0400,/* Receive Queue 1 */
+ B8_RQ2_REGS = 0x0480,/* Receive Queue 2 */
+ B8_TS1_REGS = 0x0600,/* Transmit sync queue 1 */
+ B8_TA1_REGS = 0x0680,/* Transmit async queue 1 */
+ B8_TS2_REGS = 0x0700,/* Transmit sync queue 2 */
+ B8_TA2_REGS = 0x0780,/* Transmit sync queue 2 */
+ B16_RAM_REGS = 0x0800,/* RAM Buffer Registers */
+};
+
+/* Queue Register Offsets, use Q_ADDR() to access */
+enum {
+ B8_Q_REGS = 0x0400, /* base of Queue registers */
+ Q_D = 0x00, /* 8*32 bit Current Descriptor */
+ Q_DA_L = 0x20, /* 32 bit Current Descriptor Address Low dWord */
+ Q_DA_H = 0x24, /* 32 bit Current Descriptor Address High dWord */
+ Q_AC_L = 0x28, /* 32 bit Current Address Counter Low dWord */
+ Q_AC_H = 0x2c, /* 32 bit Current Address Counter High dWord */
+ Q_BC = 0x30, /* 32 bit Current Byte Counter */
+ Q_CSR = 0x34, /* 32 bit BMU Control/Status Register */
+ Q_F = 0x38, /* 32 bit Flag Register */
+ Q_T1 = 0x3c, /* 32 bit Test Register 1 */
+ Q_T1_TR = 0x3c, /* 8 bit Test Register 1 Transfer SM */
+ Q_T1_WR = 0x3d, /* 8 bit Test Register 1 Write Descriptor SM */
+ Q_T1_RD = 0x3e, /* 8 bit Test Register 1 Read Descriptor SM */
+ Q_T1_SV = 0x3f, /* 8 bit Test Register 1 Supervisor SM */
+ Q_T2 = 0x40, /* 32 bit Test Register 2 */
+ Q_T3 = 0x44, /* 32 bit Test Register 3 */
+
+/* Yukon-2 */
+ Q_DONE = 0x24, /* 16 bit Done Index (Yukon-2 only) */
+ Q_WM = 0x40, /* 16 bit FIFO Watermark */
+ Q_AL = 0x42, /* 8 bit FIFO Alignment */
+ Q_RSP = 0x44, /* 16 bit FIFO Read Shadow Pointer */
+ Q_RSL = 0x46, /* 8 bit FIFO Read Shadow Level */
+ Q_RP = 0x48, /* 8 bit FIFO Read Pointer */
+ Q_RL = 0x4a, /* 8 bit FIFO Read Level */
+ Q_WP = 0x4c, /* 8 bit FIFO Write Pointer */
+ Q_WSP = 0x4d, /* 8 bit FIFO Write Shadow Pointer */
+ Q_WL = 0x4e, /* 8 bit FIFO Write Level */
+ Q_WSL = 0x4f, /* 8 bit FIFO Write Shadow Level */
+};
+#define Q_ADDR(reg, offs) (B8_Q_REGS + (reg) + (offs))
+
+
+/* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/
+enum {
+ Y2_B8_PREF_REGS = 0x0450,
+
+ PREF_UNIT_CTRL = 0x00, /* 32 bit Control register */
+ PREF_UNIT_LAST_IDX = 0x04, /* 16 bit Last Index */
+ PREF_UNIT_ADDR_LO = 0x08, /* 32 bit List start addr, low part */
+ PREF_UNIT_ADDR_HI = 0x0c, /* 32 bit List start addr, high part*/
+ PREF_UNIT_GET_IDX = 0x10, /* 16 bit Get Index */
+ PREF_UNIT_PUT_IDX = 0x14, /* 16 bit Put Index */
+ PREF_UNIT_FIFO_WP = 0x20, /* 8 bit FIFO write pointer */
+ PREF_UNIT_FIFO_RP = 0x24, /* 8 bit FIFO read pointer */
+ PREF_UNIT_FIFO_WM = 0x28, /* 8 bit FIFO watermark */
+ PREF_UNIT_FIFO_LEV = 0x2c, /* 8 bit FIFO level */
+
+ PREF_UNIT_MASK_IDX = 0x0fff,
+};
+#define Y2_QADDR(q,reg) (Y2_B8_PREF_REGS + (q) + (reg))
+
+/* RAM Buffer Register Offsets */
+enum {
+
+ RB_START = 0x00,/* 32 bit RAM Buffer Start Address */
+ RB_END = 0x04,/* 32 bit RAM Buffer End Address */
+ RB_WP = 0x08,/* 32 bit RAM Buffer Write Pointer */
+ RB_RP = 0x0c,/* 32 bit RAM Buffer Read Pointer */
+ RB_RX_UTPP = 0x10,/* 32 bit Rx Upper Threshold, Pause Packet */
+ RB_RX_LTPP = 0x14,/* 32 bit Rx Lower Threshold, Pause Packet */
+ RB_RX_UTHP = 0x18,/* 32 bit Rx Upper Threshold, High Prio */
+ RB_RX_LTHP = 0x1c,/* 32 bit Rx Lower Threshold, High Prio */
+ /* 0x10 - 0x1f: reserved at Tx RAM Buffer Registers */
+ RB_PC = 0x20,/* 32 bit RAM Buffer Packet Counter */
+ RB_LEV = 0x24,/* 32 bit RAM Buffer Level Register */
+ RB_CTRL = 0x28,/* 32 bit RAM Buffer Control Register */
+ RB_TST1 = 0x29,/* 8 bit RAM Buffer Test Register 1 */
+ RB_TST2 = 0x2a,/* 8 bit RAM Buffer Test Register 2 */
+};
+
+/* Receive and Transmit Queues */
+enum {
+ Q_R1 = 0x0000, /* Receive Queue 1 */
+ Q_R2 = 0x0080, /* Receive Queue 2 */
+ Q_XS1 = 0x0200, /* Synchronous Transmit Queue 1 */
+ Q_XA1 = 0x0280, /* Asynchronous Transmit Queue 1 */
+ Q_XS2 = 0x0300, /* Synchronous Transmit Queue 2 */
+ Q_XA2 = 0x0380, /* Asynchronous Transmit Queue 2 */
+};
+
+/* Different PHY Types */
+enum {
+ PHY_ADDR_MARV = 0,
+};
+
+#define RB_ADDR(offs, queue) (B16_RAM_REGS + (queue) + (offs))
+
+
+enum {
+ LNK_SYNC_INI = 0x0c30,/* 32 bit Link Sync Cnt Init Value */
+ LNK_SYNC_VAL = 0x0c34,/* 32 bit Link Sync Cnt Current Value */
+ LNK_SYNC_CTRL = 0x0c38,/* 8 bit Link Sync Cnt Control Register */
+ LNK_SYNC_TST = 0x0c39,/* 8 bit Link Sync Cnt Test Register */
+
+ LNK_LED_REG = 0x0c3c,/* 8 bit Link LED Register */
+
+/* Receive GMAC FIFO (YUKON and Yukon-2) */
+
+ RX_GMF_EA = 0x0c40,/* 32 bit Rx GMAC FIFO End Address */
+ RX_GMF_AF_THR = 0x0c44,/* 32 bit Rx GMAC FIFO Almost Full Thresh. */
+ RX_GMF_CTRL_T = 0x0c48,/* 32 bit Rx GMAC FIFO Control/Test */
+ RX_GMF_FL_MSK = 0x0c4c,/* 32 bit Rx GMAC FIFO Flush Mask */
+ RX_GMF_FL_THR = 0x0c50,/* 32 bit Rx GMAC FIFO Flush Threshold */
+ RX_GMF_TR_THR = 0x0c54,/* 32 bit Rx Truncation Threshold (Yukon-2) */
+
+ RX_GMF_VLAN = 0x0c5c,/* 32 bit Rx VLAN Type Register (Yukon-2) */
+ RX_GMF_WP = 0x0c60,/* 32 bit Rx GMAC FIFO Write Pointer */
+
+ RX_GMF_WLEV = 0x0c68,/* 32 bit Rx GMAC FIFO Write Level */
+
+ RX_GMF_RP = 0x0c70,/* 32 bit Rx GMAC FIFO Read Pointer */
+
+ RX_GMF_RLEV = 0x0c78,/* 32 bit Rx GMAC FIFO Read Level */
+};
+
+
+/* Q_BC 32 bit Current Byte Counter */
+
+/* BMU Control Status Registers */
+/* B0_R1_CSR 32 bit BMU Ctrl/Stat Rx Queue 1 */
+/* B0_R2_CSR 32 bit BMU Ctrl/Stat Rx Queue 2 */
+/* B0_XA1_CSR 32 bit BMU Ctrl/Stat Sync Tx Queue 1 */
+/* B0_XS1_CSR 32 bit BMU Ctrl/Stat Async Tx Queue 1 */
+/* B0_XA2_CSR 32 bit BMU Ctrl/Stat Sync Tx Queue 2 */
+/* B0_XS2_CSR 32 bit BMU Ctrl/Stat Async Tx Queue 2 */
+/* Q_CSR 32 bit BMU Control/Status Register */
+
+/* Rx BMU Control / Status Registers (Yukon-2) */
+enum {
+ BMU_IDLE = 1<<31, /* BMU Idle State */
+ BMU_RX_TCP_PKT = 1<<30, /* Rx TCP Packet (when RSS Hash enabled) */
+ BMU_RX_IP_PKT = 1<<29, /* Rx IP Packet (when RSS Hash enabled) */
+
+ BMU_ENA_RX_RSS_HASH = 1<<15, /* Enable Rx RSS Hash */
+ BMU_DIS_RX_RSS_HASH = 1<<14, /* Disable Rx RSS Hash */
+ BMU_ENA_RX_CHKSUM = 1<<13, /* Enable Rx TCP/IP Checksum Check */
+ BMU_DIS_RX_CHKSUM = 1<<12, /* Disable Rx TCP/IP Checksum Check */
+ BMU_CLR_IRQ_PAR = 1<<11, /* Clear IRQ on Parity errors (Rx) */
+ BMU_CLR_IRQ_TCP = 1<<11, /* Clear IRQ on TCP segment. error (Tx) */
+ BMU_CLR_IRQ_CHK = 1<<10, /* Clear IRQ Check */
+ BMU_STOP = 1<<9, /* Stop Rx/Tx Queue */
+ BMU_START = 1<<8, /* Start Rx/Tx Queue */
+ BMU_FIFO_OP_ON = 1<<7, /* FIFO Operational On */
+ BMU_FIFO_OP_OFF = 1<<6, /* FIFO Operational Off */
+ BMU_FIFO_ENA = 1<<5, /* Enable FIFO */
+ BMU_FIFO_RST = 1<<4, /* Reset FIFO */
+ BMU_OP_ON = 1<<3, /* BMU Operational On */
+ BMU_OP_OFF = 1<<2, /* BMU Operational Off */
+ BMU_RST_CLR = 1<<1, /* Clear BMU Reset (Enable) */
+ BMU_RST_SET = 1<<0, /* Set BMU Reset */
+
+ BMU_CLR_RESET = BMU_FIFO_RST | BMU_OP_OFF | BMU_RST_CLR,
+ BMU_OPER_INIT = BMU_CLR_IRQ_PAR | BMU_CLR_IRQ_CHK | BMU_START |
+ BMU_FIFO_ENA | BMU_OP_ON,
+};
+
+/* Tx BMU Control / Status Registers (Yukon-2) */
+ /* Bit 31: same as for Rx */
+enum {
+ BMU_TX_IPIDINCR_ON = 1<<13, /* Enable IP ID Increment */
+ BMU_TX_IPIDINCR_OFF = 1<<12, /* Disable IP ID Increment */
+ BMU_TX_CLR_IRQ_TCP = 1<<11, /* Clear IRQ on TCP segment length mismatch */
+};
+
+/* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/
+/* PREF_UNIT_CTRL 32 bit Prefetch Control register */
+enum {
+ PREF_UNIT_OP_ON = 1<<3, /* prefetch unit operational */
+ PREF_UNIT_OP_OFF = 1<<2, /* prefetch unit not operational */
+ PREF_UNIT_RST_CLR = 1<<1, /* Clear Prefetch Unit Reset */
+ PREF_UNIT_RST_SET = 1<<0, /* Set Prefetch Unit Reset */
+};
+
+/* RAM Buffer Register Offsets, use RB_ADDR(Queue, Offs) to access */
+/* RB_START 32 bit RAM Buffer Start Address */
+/* RB_END 32 bit RAM Buffer End Address */
+/* RB_WP 32 bit RAM Buffer Write Pointer */
+/* RB_RP 32 bit RAM Buffer Read Pointer */
+/* RB_RX_UTPP 32 bit Rx Upper Threshold, Pause Pack */
+/* RB_RX_LTPP 32 bit Rx Lower Threshold, Pause Pack */
+/* RB_RX_UTHP 32 bit Rx Upper Threshold, High Prio */
+/* RB_RX_LTHP 32 bit Rx Lower Threshold, High Prio */
+/* RB_PC 32 bit RAM Buffer Packet Counter */
+/* RB_LEV 32 bit RAM Buffer Level Register */
+
+#define RB_MSK 0x0007ffff /* Bit 18.. 0: RAM Buffer Pointer Bits */
+/* RB_TST2 8 bit RAM Buffer Test Register 2 */
+/* RB_TST1 8 bit RAM Buffer Test Register 1 */
+
+/* RB_CTRL 8 bit RAM Buffer Control Register */
+enum {
+ RB_ENA_STFWD = 1<<5, /* Enable Store & Forward */
+ RB_DIS_STFWD = 1<<4, /* Disable Store & Forward */
+ RB_ENA_OP_MD = 1<<3, /* Enable Operation Mode */
+ RB_DIS_OP_MD = 1<<2, /* Disable Operation Mode */
+ RB_RST_CLR = 1<<1, /* Clear RAM Buf STM Reset */
+ RB_RST_SET = 1<<0, /* Set RAM Buf STM Reset */
+};
+
+
+/* Transmit GMAC FIFO (YUKON only) */
+enum {
+ TX_GMF_EA = 0x0d40,/* 32 bit Tx GMAC FIFO End Address */
+ TX_GMF_AE_THR = 0x0d44,/* 32 bit Tx GMAC FIFO Almost Empty Thresh.*/
+ TX_GMF_CTRL_T = 0x0d48,/* 32 bit Tx GMAC FIFO Control/Test */
+
+ TX_GMF_WP = 0x0d60,/* 32 bit Tx GMAC FIFO Write Pointer */
+ TX_GMF_WSP = 0x0d64,/* 32 bit Tx GMAC FIFO Write Shadow Ptr. */
+ TX_GMF_WLEV = 0x0d68,/* 32 bit Tx GMAC FIFO Write Level */
+
+ TX_GMF_RP = 0x0d70,/* 32 bit Tx GMAC FIFO Read Pointer */
+ TX_GMF_RSTP = 0x0d74,/* 32 bit Tx GMAC FIFO Restart Pointer */
+ TX_GMF_RLEV = 0x0d78,/* 32 bit Tx GMAC FIFO Read Level */
+};
+
+/* Descriptor Poll Timer Registers */
+enum {
+ B28_DPT_INI = 0x0e00,/* 24 bit Descriptor Poll Timer Init Val */
+ B28_DPT_VAL = 0x0e04,/* 24 bit Descriptor Poll Timer Curr Val */
+ B28_DPT_CTRL = 0x0e08,/* 8 bit Descriptor Poll Timer Ctrl Reg */
+
+ B28_DPT_TST = 0x0e0a,/* 8 bit Descriptor Poll Timer Test Reg */
+};
+
+/* Time Stamp Timer Registers (YUKON only) */
+enum {
+ GMAC_TI_ST_VAL = 0x0e14,/* 32 bit Time Stamp Timer Curr Val */
+ GMAC_TI_ST_CTRL = 0x0e18,/* 8 bit Time Stamp Timer Ctrl Reg */
+ GMAC_TI_ST_TST = 0x0e1a,/* 8 bit Time Stamp Timer Test Reg */
+};
+
+/* Polling Unit Registers (Yukon-2 only) */
+enum {
+ POLL_CTRL = 0x0e20, /* 32 bit Polling Unit Control Reg */
+ POLL_LAST_IDX = 0x0e24,/* 16 bit Polling Unit List Last Index */
+
+ POLL_LIST_ADDR_LO= 0x0e28,/* 32 bit Poll. List Start Addr (low) */
+ POLL_LIST_ADDR_HI= 0x0e2c,/* 32 bit Poll. List Start Addr (high) */
+};
+
+/* ASF Subsystem Registers (Yukon-2 only) */
+enum {
+ B28_Y2_SMB_CONFIG = 0x0e40,/* 32 bit ASF SMBus Config Register */
+ B28_Y2_SMB_CSD_REG = 0x0e44,/* 32 bit ASF SMB Control/Status/Data */
+ B28_Y2_ASF_IRQ_V_BASE=0x0e60,/* 32 bit ASF IRQ Vector Base */
+
+ B28_Y2_ASF_STAT_CMD= 0x0e68,/* 32 bit ASF Status and Command Reg */
+ B28_Y2_ASF_HOST_COM= 0x0e6c,/* 32 bit ASF Host Communication Reg */
+ B28_Y2_DATA_REG_1 = 0x0e70,/* 32 bit ASF/Host Data Register 1 */
+ B28_Y2_DATA_REG_2 = 0x0e74,/* 32 bit ASF/Host Data Register 2 */
+ B28_Y2_DATA_REG_3 = 0x0e78,/* 32 bit ASF/Host Data Register 3 */
+ B28_Y2_DATA_REG_4 = 0x0e7c,/* 32 bit ASF/Host Data Register 4 */
+};
+
+/* Status BMU Registers (Yukon-2 only)*/
+enum {
+ STAT_CTRL = 0x0e80,/* 32 bit Status BMU Control Reg */
+ STAT_LAST_IDX = 0x0e84,/* 16 bit Status BMU Last Index */
+
+ STAT_LIST_ADDR_LO= 0x0e88,/* 32 bit Status List Start Addr (low) */
+ STAT_LIST_ADDR_HI= 0x0e8c,/* 32 bit Status List Start Addr (high) */
+ STAT_TXA1_RIDX = 0x0e90,/* 16 bit Status TxA1 Report Index Reg */
+ STAT_TXS1_RIDX = 0x0e92,/* 16 bit Status TxS1 Report Index Reg */
+ STAT_TXA2_RIDX = 0x0e94,/* 16 bit Status TxA2 Report Index Reg */
+ STAT_TXS2_RIDX = 0x0e96,/* 16 bit Status TxS2 Report Index Reg */
+ STAT_TX_IDX_TH = 0x0e98,/* 16 bit Status Tx Index Threshold Reg */
+ STAT_PUT_IDX = 0x0e9c,/* 16 bit Status Put Index Reg */
+
+/* FIFO Control/Status Registers (Yukon-2 only)*/
+ STAT_FIFO_WP = 0x0ea0,/* 8 bit Status FIFO Write Pointer Reg */
+ STAT_FIFO_RP = 0x0ea4,/* 8 bit Status FIFO Read Pointer Reg */
+ STAT_FIFO_RSP = 0x0ea6,/* 8 bit Status FIFO Read Shadow Ptr */
+ STAT_FIFO_LEVEL = 0x0ea8,/* 8 bit Status FIFO Level Reg */
+ STAT_FIFO_SHLVL = 0x0eaa,/* 8 bit Status FIFO Shadow Level Reg */
+ STAT_FIFO_WM = 0x0eac,/* 8 bit Status FIFO Watermark Reg */
+ STAT_FIFO_ISR_WM= 0x0ead,/* 8 bit Status FIFO ISR Watermark Reg */
+
+/* Level and ISR Timer Registers (Yukon-2 only)*/
+ STAT_LEV_TIMER_INI= 0x0eb0,/* 32 bit Level Timer Init. Value Reg */
+ STAT_LEV_TIMER_CNT= 0x0eb4,/* 32 bit Level Timer Counter Reg */
+ STAT_LEV_TIMER_CTRL= 0x0eb8,/* 8 bit Level Timer Control Reg */
+ STAT_LEV_TIMER_TEST= 0x0eb9,/* 8 bit Level Timer Test Reg */
+ STAT_TX_TIMER_INI = 0x0ec0,/* 32 bit Tx Timer Init. Value Reg */
+ STAT_TX_TIMER_CNT = 0x0ec4,/* 32 bit Tx Timer Counter Reg */
+ STAT_TX_TIMER_CTRL = 0x0ec8,/* 8 bit Tx Timer Control Reg */
+ STAT_TX_TIMER_TEST = 0x0ec9,/* 8 bit Tx Timer Test Reg */
+ STAT_ISR_TIMER_INI = 0x0ed0,/* 32 bit ISR Timer Init. Value Reg */
+ STAT_ISR_TIMER_CNT = 0x0ed4,/* 32 bit ISR Timer Counter Reg */
+ STAT_ISR_TIMER_CTRL= 0x0ed8,/* 8 bit ISR Timer Control Reg */
+ STAT_ISR_TIMER_TEST= 0x0ed9,/* 8 bit ISR Timer Test Reg */
+};
+
+enum {
+ LINKLED_OFF = 0x01,
+ LINKLED_ON = 0x02,
+ LINKLED_LINKSYNC_OFF = 0x04,
+ LINKLED_LINKSYNC_ON = 0x08,
+ LINKLED_BLINK_OFF = 0x10,
+ LINKLED_BLINK_ON = 0x20,
+};
+
+/* GMAC and GPHY Control Registers (YUKON only) */
+enum {
+ GMAC_CTRL = 0x0f00,/* 32 bit GMAC Control Reg */
+ GPHY_CTRL = 0x0f04,/* 32 bit GPHY Control Reg */
+ GMAC_IRQ_SRC = 0x0f08,/* 8 bit GMAC Interrupt Source Reg */
+ GMAC_IRQ_MSK = 0x0f0c,/* 8 bit GMAC Interrupt Mask Reg */
+ GMAC_LINK_CTRL = 0x0f10,/* 16 bit Link Control Reg */
+
+/* Wake-up Frame Pattern Match Control Registers (YUKON only) */
+
+ WOL_REG_OFFS = 0x20,/* HW-Bug: Address is + 0x20 against spec. */
+
+ WOL_CTRL_STAT = 0x0f20,/* 16 bit WOL Control/Status Reg */
+ WOL_MATCH_CTL = 0x0f22,/* 8 bit WOL Match Control Reg */
+ WOL_MATCH_RES = 0x0f23,/* 8 bit WOL Match Result Reg */
+ WOL_MAC_ADDR = 0x0f24,/* 32 bit WOL MAC Address */
+ WOL_PATT_PME = 0x0f2a,/* 8 bit WOL PME Match Enable (Yukon-2) */
+ WOL_PATT_ASFM = 0x0f2b,/* 8 bit WOL ASF Match Enable (Yukon-2) */
+ WOL_PATT_RPTR = 0x0f2c,/* 8 bit WOL Pattern Read Pointer */
+
+/* WOL Pattern Length Registers (YUKON only) */
+
+ WOL_PATT_LEN_LO = 0x0f30,/* 32 bit WOL Pattern Length 3..0 */
+ WOL_PATT_LEN_HI = 0x0f34,/* 24 bit WOL Pattern Length 6..4 */
+
+/* WOL Pattern Counter Registers (YUKON only) */
+
+
+ WOL_PATT_CNT_0 = 0x0f38,/* 32 bit WOL Pattern Counter 3..0 */
+ WOL_PATT_CNT_4 = 0x0f3c,/* 24 bit WOL Pattern Counter 6..4 */
+};
+
+enum {
+ WOL_PATT_RAM_1 = 0x1000,/* WOL Pattern RAM Link 1 */
+ WOL_PATT_RAM_2 = 0x1400,/* WOL Pattern RAM Link 2 */
+};
+
+enum {
+ BASE_GMAC_1 = 0x2800,/* GMAC 1 registers */
+ BASE_GMAC_2 = 0x3800,/* GMAC 2 registers */
+};
+
+/*
+ * Marvel-PHY Registers, indirect addressed over GMAC
+ */
+enum {
+ PHY_MARV_CTRL = 0x00,/* 16 bit r/w PHY Control Register */
+ PHY_MARV_STAT = 0x01,/* 16 bit r/o PHY Status Register */
+ PHY_MARV_ID0 = 0x02,/* 16 bit r/o PHY ID0 Register */
+ PHY_MARV_ID1 = 0x03,/* 16 bit r/o PHY ID1 Register */
+ PHY_MARV_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */
+ PHY_MARV_AUNE_LP = 0x05,/* 16 bit r/o Link Part Ability Reg */
+ PHY_MARV_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */
+ PHY_MARV_NEPG = 0x07,/* 16 bit r/w Next Page Register */
+ PHY_MARV_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner */
+ /* Marvel-specific registers */
+ PHY_MARV_1000T_CTRL = 0x09,/* 16 bit r/w 1000Base-T Control Reg */
+ PHY_MARV_1000T_STAT = 0x0a,/* 16 bit r/o 1000Base-T Status Reg */
+ PHY_MARV_EXT_STAT = 0x0f,/* 16 bit r/o Extended Status Reg */
+ PHY_MARV_PHY_CTRL = 0x10,/* 16 bit r/w PHY Specific Ctrl Reg */
+ PHY_MARV_PHY_STAT = 0x11,/* 16 bit r/o PHY Specific Stat Reg */
+ PHY_MARV_INT_MASK = 0x12,/* 16 bit r/w Interrupt Mask Reg */
+ PHY_MARV_INT_STAT = 0x13,/* 16 bit r/o Interrupt Status Reg */
+ PHY_MARV_EXT_CTRL = 0x14,/* 16 bit r/w Ext. PHY Specific Ctrl */
+ PHY_MARV_RXE_CNT = 0x15,/* 16 bit r/w Receive Error Counter */
+ PHY_MARV_EXT_ADR = 0x16,/* 16 bit r/w Ext. Ad. for Cable Diag. */
+ PHY_MARV_PORT_IRQ = 0x17,/* 16 bit r/o Port 0 IRQ (88E1111 only) */
+ PHY_MARV_LED_CTRL = 0x18,/* 16 bit r/w LED Control Reg */
+ PHY_MARV_LED_OVER = 0x19,/* 16 bit r/w Manual LED Override Reg */
+ PHY_MARV_EXT_CTRL_2 = 0x1a,/* 16 bit r/w Ext. PHY Specific Ctrl 2 */
+ PHY_MARV_EXT_P_STAT = 0x1b,/* 16 bit r/w Ext. PHY Spec. Stat Reg */
+ PHY_MARV_CABLE_DIAG = 0x1c,/* 16 bit r/o Cable Diagnostic Reg */
+ PHY_MARV_PAGE_ADDR = 0x1d,/* 16 bit r/w Extended Page Address Reg */
+ PHY_MARV_PAGE_DATA = 0x1e,/* 16 bit r/w Extended Page Data Reg */
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+ PHY_MARV_FE_LED_PAR = 0x16,/* 16 bit r/w LED Parallel Select Reg. */
+ PHY_MARV_FE_LED_SER = 0x17,/* 16 bit r/w LED Stream Select S. LED */
+ PHY_MARV_FE_VCT_TX = 0x1a,/* 16 bit r/w VCT Reg. for TXP/N Pins */
+ PHY_MARV_FE_VCT_RX = 0x1b,/* 16 bit r/o VCT Reg. for RXP/N Pins */
+ PHY_MARV_FE_SPEC_2 = 0x1c,/* 16 bit r/w Specific Control Reg. 2 */
+};
+
+enum {
+ PHY_CT_RESET = 1<<15, /* Bit 15: (sc) clear all PHY related regs */
+ PHY_CT_LOOP = 1<<14, /* Bit 14: enable Loopback over PHY */
+ PHY_CT_SPS_LSB = 1<<13, /* Bit 13: Speed select, lower bit */
+ PHY_CT_ANE = 1<<12, /* Bit 12: Auto-Negotiation Enabled */
+ PHY_CT_PDOWN = 1<<11, /* Bit 11: Power Down Mode */
+ PHY_CT_ISOL = 1<<10, /* Bit 10: Isolate Mode */
+ PHY_CT_RE_CFG = 1<<9, /* Bit 9: (sc) Restart Auto-Negotiation */
+ PHY_CT_DUP_MD = 1<<8, /* Bit 8: Duplex Mode */
+ PHY_CT_COL_TST = 1<<7, /* Bit 7: Collision Test enabled */
+ PHY_CT_SPS_MSB = 1<<6, /* Bit 6: Speed select, upper bit */
+};
+
+enum {
+ PHY_CT_SP1000 = PHY_CT_SPS_MSB, /* enable speed of 1000 Mbps */
+ PHY_CT_SP100 = PHY_CT_SPS_LSB, /* enable speed of 100 Mbps */
+ PHY_CT_SP10 = 0, /* enable speed of 10 Mbps */
+};
+
+enum {
+ PHY_ST_EXT_ST = 1<<8, /* Bit 8: Extended Status Present */
+
+ PHY_ST_PRE_SUP = 1<<6, /* Bit 6: Preamble Suppression */
+ PHY_ST_AN_OVER = 1<<5, /* Bit 5: Auto-Negotiation Over */
+ PHY_ST_REM_FLT = 1<<4, /* Bit 4: Remote Fault Condition Occured */
+ PHY_ST_AN_CAP = 1<<3, /* Bit 3: Auto-Negotiation Capability */
+ PHY_ST_LSYNC = 1<<2, /* Bit 2: Link Synchronized */
+ PHY_ST_JAB_DET = 1<<1, /* Bit 1: Jabber Detected */
+ PHY_ST_EXT_REG = 1<<0, /* Bit 0: Extended Register available */
+};
+
+enum {
+ PHY_I1_OUI_MSK = 0x3f<<10, /* Bit 15..10: Organization Unique ID */
+ PHY_I1_MOD_NUM = 0x3f<<4, /* Bit 9.. 4: Model Number */
+ PHY_I1_REV_MSK = 0xf, /* Bit 3.. 0: Revision Number */
+};
+
+/* different Marvell PHY Ids */
+enum {
+ PHY_MARV_ID0_VAL= 0x0141, /* Marvell Unique Identifier */
+
+ PHY_BCOM_ID1_A1 = 0x6041,
+ PHY_BCOM_ID1_B2 = 0x6043,
+ PHY_BCOM_ID1_C0 = 0x6044,
+ PHY_BCOM_ID1_C5 = 0x6047,
+
+ PHY_MARV_ID1_B0 = 0x0C23, /* Yukon (PHY 88E1011) */
+ PHY_MARV_ID1_B2 = 0x0C25, /* Yukon-Plus (PHY 88E1011) */
+ PHY_MARV_ID1_C2 = 0x0CC2, /* Yukon-EC (PHY 88E1111) */
+ PHY_MARV_ID1_Y2 = 0x0C91, /* Yukon-2 (PHY 88E1112) */
+};
+
+/* Advertisement register bits */
+enum {
+ PHY_AN_NXT_PG = 1<<15, /* Bit 15: Request Next Page */
+ PHY_AN_ACK = 1<<14, /* Bit 14: (ro) Acknowledge Received */
+ PHY_AN_RF = 1<<13, /* Bit 13: Remote Fault Bits */
+
+ PHY_AN_PAUSE_ASYM = 1<<11,/* Bit 11: Try for asymmetric */
+ PHY_AN_PAUSE_CAP = 1<<10, /* Bit 10: Try for pause */
+ PHY_AN_100BASE4 = 1<<9, /* Bit 9: Try for 100mbps 4k packets */
+ PHY_AN_100FULL = 1<<8, /* Bit 8: Try for 100mbps full-duplex */
+ PHY_AN_100HALF = 1<<7, /* Bit 7: Try for 100mbps half-duplex */
+ PHY_AN_10FULL = 1<<6, /* Bit 6: Try for 10mbps full-duplex */
+ PHY_AN_10HALF = 1<<5, /* Bit 5: Try for 10mbps half-duplex */
+ PHY_AN_CSMA = 1<<0, /* Bit 0: Only selector supported */
+ PHY_AN_SEL = 0x1f, /* Bit 4..0: Selector Field, 00001=Ethernet*/
+ PHY_AN_FULL = PHY_AN_100FULL | PHY_AN_10FULL | PHY_AN_CSMA,
+ PHY_AN_ALL = PHY_AN_10HALF | PHY_AN_10FULL |
+ PHY_AN_100HALF | PHY_AN_100FULL,
+};
+
+/***** PHY_BCOM_1000T_STAT 16 bit r/o 1000Base-T Status Reg *****/
+/***** PHY_MARV_1000T_STAT 16 bit r/o 1000Base-T Status Reg *****/
+enum {
+ PHY_B_1000S_MSF = 1<<15, /* Bit 15: Master/Slave Fault */
+ PHY_B_1000S_MSR = 1<<14, /* Bit 14: Master/Slave Result */
+ PHY_B_1000S_LRS = 1<<13, /* Bit 13: Local Receiver Status */
+ PHY_B_1000S_RRS = 1<<12, /* Bit 12: Remote Receiver Status */
+ PHY_B_1000S_LP_FD = 1<<11, /* Bit 11: Link Partner can FD */
+ PHY_B_1000S_LP_HD = 1<<10, /* Bit 10: Link Partner can HD */
+ /* Bit 9..8: reserved */
+ PHY_B_1000S_IEC = 0xff, /* Bit 7..0: Idle Error Count */
+};
+
+/** Marvell-Specific */
+enum {
+ PHY_M_AN_NXT_PG = 1<<15, /* Request Next Page */
+ PHY_M_AN_ACK = 1<<14, /* (ro) Acknowledge Received */
+ PHY_M_AN_RF = 1<<13, /* Remote Fault */
+
+ PHY_M_AN_ASP = 1<<11, /* Asymmetric Pause */
+ PHY_M_AN_PC = 1<<10, /* MAC Pause implemented */
+ PHY_M_AN_100_T4 = 1<<9, /* Not cap. 100Base-T4 (always 0) */
+ PHY_M_AN_100_FD = 1<<8, /* Advertise 100Base-TX Full Duplex */
+ PHY_M_AN_100_HD = 1<<7, /* Advertise 100Base-TX Half Duplex */
+ PHY_M_AN_10_FD = 1<<6, /* Advertise 10Base-TX Full Duplex */
+ PHY_M_AN_10_HD = 1<<5, /* Advertise 10Base-TX Half Duplex */
+ PHY_M_AN_SEL_MSK =0x1f<<4, /* Bit 4.. 0: Selector Field Mask */
+};
+
+/* special defines for FIBER (88E1011S only) */
+enum {
+ PHY_M_AN_ASP_X = 1<<8, /* Asymmetric Pause */
+ PHY_M_AN_PC_X = 1<<7, /* MAC Pause implemented */
+ PHY_M_AN_1000X_AHD = 1<<6, /* Advertise 10000Base-X Half Duplex */
+ PHY_M_AN_1000X_AFD = 1<<5, /* Advertise 10000Base-X Full Duplex */
+};
+
+/* Pause Bits (PHY_M_AN_ASP_X and PHY_M_AN_PC_X) encoding */
+enum {
+ PHY_M_P_NO_PAUSE_X = 0<<7,/* Bit 8.. 7: no Pause Mode */
+ PHY_M_P_SYM_MD_X = 1<<7, /* Bit 8.. 7: symmetric Pause Mode */
+ PHY_M_P_ASYM_MD_X = 2<<7,/* Bit 8.. 7: asymmetric Pause Mode */
+ PHY_M_P_BOTH_MD_X = 3<<7,/* Bit 8.. 7: both Pause Mode */
+};
+
+/***** PHY_MARV_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/
+enum {
+ PHY_M_1000C_TEST = 7<<13,/* Bit 15..13: Test Modes */
+ PHY_M_1000C_MSE = 1<<12, /* Manual Master/Slave Enable */
+ PHY_M_1000C_MSC = 1<<11, /* M/S Configuration (1=Master) */
+ PHY_M_1000C_MPD = 1<<10, /* Multi-Port Device */
+ PHY_M_1000C_AFD = 1<<9, /* Advertise Full Duplex */
+ PHY_M_1000C_AHD = 1<<8, /* Advertise Half Duplex */
+};
+
+/***** PHY_MARV_PHY_CTRL 16 bit r/w PHY Specific Ctrl Reg *****/
+enum {
+ PHY_M_PC_TX_FFD_MSK = 3<<14,/* Bit 15..14: Tx FIFO Depth Mask */
+ PHY_M_PC_RX_FFD_MSK = 3<<12,/* Bit 13..12: Rx FIFO Depth Mask */
+ PHY_M_PC_ASS_CRS_TX = 1<<11, /* Assert CRS on Transmit */
+ PHY_M_PC_FL_GOOD = 1<<10, /* Force Link Good */
+ PHY_M_PC_EN_DET_MSK = 3<<8,/* Bit 9.. 8: Energy Detect Mask */
+ PHY_M_PC_ENA_EXT_D = 1<<7, /* Enable Ext. Distance (10BT) */
+ PHY_M_PC_MDIX_MSK = 3<<5,/* Bit 6.. 5: MDI/MDIX Config. Mask */
+ PHY_M_PC_DIS_125CLK = 1<<4, /* Disable 125 CLK */
+ PHY_M_PC_MAC_POW_UP = 1<<3, /* MAC Power up */
+ PHY_M_PC_SQE_T_ENA = 1<<2, /* SQE Test Enabled */
+ PHY_M_PC_POL_R_DIS = 1<<1, /* Polarity Reversal Disabled */
+ PHY_M_PC_DIS_JABBER = 1<<0, /* Disable Jabber */
+};
+
+enum {
+ PHY_M_PC_EN_DET = 2<<8, /* Energy Detect (Mode 1) */
+ PHY_M_PC_EN_DET_PLUS = 3<<8, /* Energy Detect Plus (Mode 2) */
+};
+
+#define PHY_M_PC_MDI_XMODE(x) (((x)<<5) & PHY_M_PC_MDIX_MSK)
+
+enum {
+ PHY_M_PC_MAN_MDI = 0, /* 00 = Manual MDI configuration */
+ PHY_M_PC_MAN_MDIX = 1, /* 01 = Manual MDIX configuration */
+ PHY_M_PC_ENA_AUTO = 3, /* 11 = Enable Automatic Crossover */
+};
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+enum {
+ PHY_M_PC_ENA_DTE_DT = 1<<15, /* Enable Data Terminal Equ. (DTE) Detect */
+ PHY_M_PC_ENA_ENE_DT = 1<<14, /* Enable Energy Detect (sense & pulse) */
+ PHY_M_PC_DIS_NLP_CK = 1<<13, /* Disable Normal Link Puls (NLP) Check */
+ PHY_M_PC_ENA_LIP_NP = 1<<12, /* Enable Link Partner Next Page Reg. */
+ PHY_M_PC_DIS_NLP_GN = 1<<11, /* Disable Normal Link Puls Generation */
+
+ PHY_M_PC_DIS_SCRAMB = 1<<9, /* Disable Scrambler */
+ PHY_M_PC_DIS_FEFI = 1<<8, /* Disable Far End Fault Indic. (FEFI) */
+
+ PHY_M_PC_SH_TP_SEL = 1<<6, /* Shielded Twisted Pair Select */
+ PHY_M_PC_RX_FD_MSK = 3<<2,/* Bit 3.. 2: Rx FIFO Depth Mask */
+};
+
+/***** PHY_MARV_PHY_STAT 16 bit r/o PHY Specific Status Reg *****/
+enum {
+ PHY_M_PS_SPEED_MSK = 3<<14, /* Bit 15..14: Speed Mask */
+ PHY_M_PS_SPEED_1000 = 1<<15, /* 10 = 1000 Mbps */
+ PHY_M_PS_SPEED_100 = 1<<14, /* 01 = 100 Mbps */
+ PHY_M_PS_SPEED_10 = 0, /* 00 = 10 Mbps */
+ PHY_M_PS_FULL_DUP = 1<<13, /* Full Duplex */
+ PHY_M_PS_PAGE_REC = 1<<12, /* Page Received */
+ PHY_M_PS_SPDUP_RES = 1<<11, /* Speed & Duplex Resolved */
+ PHY_M_PS_LINK_UP = 1<<10, /* Link Up */
+ PHY_M_PS_CABLE_MSK = 7<<7, /* Bit 9.. 7: Cable Length Mask */
+ PHY_M_PS_MDI_X_STAT = 1<<6, /* MDI Crossover Stat (1=MDIX) */
+ PHY_M_PS_DOWNS_STAT = 1<<5, /* Downshift Status (1=downsh.) */
+ PHY_M_PS_ENDET_STAT = 1<<4, /* Energy Detect Status (1=act) */
+ PHY_M_PS_TX_P_EN = 1<<3, /* Tx Pause Enabled */
+ PHY_M_PS_RX_P_EN = 1<<2, /* Rx Pause Enabled */
+ PHY_M_PS_POL_REV = 1<<1, /* Polarity Reversed */
+ PHY_M_PS_JABBER = 1<<0, /* Jabber */
+};
+
+#define PHY_M_PS_PAUSE_MSK (PHY_M_PS_TX_P_EN | PHY_M_PS_RX_P_EN)
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+enum {
+ PHY_M_PS_DTE_DETECT = 1<<15, /* Data Terminal Equipment (DTE) Detected */
+ PHY_M_PS_RES_SPEED = 1<<14, /* Resolved Speed (1=100 Mbps, 0=10 Mbps */
+};
+
+enum {
+ PHY_M_IS_AN_ERROR = 1<<15, /* Auto-Negotiation Error */
+ PHY_M_IS_LSP_CHANGE = 1<<14, /* Link Speed Changed */
+ PHY_M_IS_DUP_CHANGE = 1<<13, /* Duplex Mode Changed */
+ PHY_M_IS_AN_PR = 1<<12, /* Page Received */
+ PHY_M_IS_AN_COMPL = 1<<11, /* Auto-Negotiation Completed */
+ PHY_M_IS_LST_CHANGE = 1<<10, /* Link Status Changed */
+ PHY_M_IS_SYMB_ERROR = 1<<9, /* Symbol Error */
+ PHY_M_IS_FALSE_CARR = 1<<8, /* False Carrier */
+ PHY_M_IS_FIFO_ERROR = 1<<7, /* FIFO Overflow/Underrun Error */
+ PHY_M_IS_MDI_CHANGE = 1<<6, /* MDI Crossover Changed */
+ PHY_M_IS_DOWNSH_DET = 1<<5, /* Downshift Detected */
+ PHY_M_IS_END_CHANGE = 1<<4, /* Energy Detect Changed */
+
+ PHY_M_IS_DTE_CHANGE = 1<<2, /* DTE Power Det. Status Changed */
+ PHY_M_IS_POL_CHANGE = 1<<1, /* Polarity Changed */
+ PHY_M_IS_JABBER = 1<<0, /* Jabber */
+
+ PHY_M_DEF_MSK = PHY_M_IS_LSP_CHANGE | PHY_M_IS_LST_CHANGE
+ | PHY_M_IS_FIFO_ERROR,
+ PHY_M_AN_MSK = PHY_M_IS_AN_ERROR | PHY_M_IS_AN_COMPL,
+};
+
+
+/***** PHY_MARV_EXT_CTRL 16 bit r/w Ext. PHY Specific Ctrl *****/
+enum {
+ PHY_M_EC_ENA_BC_EXT = 1<<15, /* Enable Block Carr. Ext. (88E1111 only) */
+ PHY_M_EC_ENA_LIN_LB = 1<<14, /* Enable Line Loopback (88E1111 only) */
+
+ PHY_M_EC_DIS_LINK_P = 1<<12, /* Disable Link Pulses (88E1111 only) */
+ PHY_M_EC_M_DSC_MSK = 3<<10, /* Bit 11..10: Master Downshift Counter */
+ /* (88E1011 only) */
+ PHY_M_EC_S_DSC_MSK = 3<<8,/* Bit 9.. 8: Slave Downshift Counter */
+ /* (88E1011 only) */
+ PHY_M_EC_M_DSC_MSK2 = 7<<9,/* Bit 11.. 9: Master Downshift Counter */
+ /* (88E1111 only) */
+ PHY_M_EC_DOWN_S_ENA = 1<<8, /* Downshift Enable (88E1111 only) */
+ /* !!! Errata in spec. (1 = disable) */
+ PHY_M_EC_RX_TIM_CT = 1<<7, /* RGMII Rx Timing Control*/
+ PHY_M_EC_MAC_S_MSK = 7<<4,/* Bit 6.. 4: Def. MAC interface speed */
+ PHY_M_EC_FIB_AN_ENA = 1<<3, /* Fiber Auto-Neg. Enable (88E1011S only) */
+ PHY_M_EC_DTE_D_ENA = 1<<2, /* DTE Detect Enable (88E1111 only) */
+ PHY_M_EC_TX_TIM_CT = 1<<1, /* RGMII Tx Timing Control */
+ PHY_M_EC_TRANS_DIS = 1<<0, /* Transmitter Disable (88E1111 only) */};
+
+#define PHY_M_EC_M_DSC(x) ((x)<<10 & PHY_M_EC_M_DSC_MSK)
+ /* 00=1x; 01=2x; 10=3x; 11=4x */
+#define PHY_M_EC_S_DSC(x) ((x)<<8 & PHY_M_EC_S_DSC_MSK)
+ /* 00=dis; 01=1x; 10=2x; 11=3x */
+#define PHY_M_EC_DSC_2(x) ((x)<<9 & PHY_M_EC_M_DSC_MSK2)
+ /* 000=1x; 001=2x; 010=3x; 011=4x */
+#define PHY_M_EC_MAC_S(x) ((x)<<4 & PHY_M_EC_MAC_S_MSK)
+ /* 01X=0; 110=2.5; 111=25 (MHz) */
+
+/* for Yukon-2 Gigabit Ethernet PHY (88E1112 only) */
+enum {
+ PHY_M_PC_DIS_LINK_Pa = 1<<15,/* Disable Link Pulses */
+ PHY_M_PC_DSC_MSK = 7<<12,/* Bit 14..12: Downshift Counter */
+ PHY_M_PC_DOWN_S_ENA = 1<<11,/* Downshift Enable */
+};
+/* !!! Errata in spec. (1 = disable) */
+
+#define PHY_M_PC_DSC(x) (((x)<<12) & PHY_M_PC_DSC_MSK)
+ /* 100=5x; 101=6x; 110=7x; 111=8x */
+enum {
+ MAC_TX_CLK_0_MHZ = 2,
+ MAC_TX_CLK_2_5_MHZ = 6,
+ MAC_TX_CLK_25_MHZ = 7,
+};
+
+/***** PHY_MARV_LED_CTRL 16 bit r/w LED Control Reg *****/
+enum {
+ PHY_M_LEDC_DIS_LED = 1<<15, /* Disable LED */
+ PHY_M_LEDC_PULS_MSK = 7<<12,/* Bit 14..12: Pulse Stretch Mask */
+ PHY_M_LEDC_F_INT = 1<<11, /* Force Interrupt */
+ PHY_M_LEDC_BL_R_MSK = 7<<8,/* Bit 10.. 8: Blink Rate Mask */
+ PHY_M_LEDC_DP_C_LSB = 1<<7, /* Duplex Control (LSB, 88E1111 only) */
+ PHY_M_LEDC_TX_C_LSB = 1<<6, /* Tx Control (LSB, 88E1111 only) */
+ PHY_M_LEDC_LK_C_MSK = 7<<3,/* Bit 5.. 3: Link Control Mask */
+ /* (88E1111 only) */
+};
+
+enum {
+ PHY_M_LEDC_LINK_MSK = 3<<3,/* Bit 4.. 3: Link Control Mask */
+ /* (88E1011 only) */
+ PHY_M_LEDC_DP_CTRL = 1<<2, /* Duplex Control */
+ PHY_M_LEDC_DP_C_MSB = 1<<2, /* Duplex Control (MSB, 88E1111 only) */
+ PHY_M_LEDC_RX_CTRL = 1<<1, /* Rx Activity / Link */
+ PHY_M_LEDC_TX_CTRL = 1<<0, /* Tx Activity / Link */
+ PHY_M_LEDC_TX_C_MSB = 1<<0, /* Tx Control (MSB, 88E1111 only) */
+};
+
+#define PHY_M_LED_PULS_DUR(x) (((x)<<12) & PHY_M_LEDC_PULS_MSK)
+
+/***** PHY_MARV_PHY_STAT (page 3)16 bit r/w Polarity Control Reg. *****/
+enum {
+ PHY_M_POLC_LS1M_MSK = 0xf<<12, /* Bit 15..12: LOS,STAT1 Mix % Mask */
+ PHY_M_POLC_IS0M_MSK = 0xf<<8, /* Bit 11.. 8: INIT,STAT0 Mix % Mask */
+ PHY_M_POLC_LOS_MSK = 0x3<<6, /* Bit 7.. 6: LOS Pol. Ctrl. Mask */
+ PHY_M_POLC_INIT_MSK = 0x3<<4, /* Bit 5.. 4: INIT Pol. Ctrl. Mask */
+ PHY_M_POLC_STA1_MSK = 0x3<<2, /* Bit 3.. 2: STAT1 Pol. Ctrl. Mask */
+ PHY_M_POLC_STA0_MSK = 0x3, /* Bit 1.. 0: STAT0 Pol. Ctrl. Mask */
+};
+
+#define PHY_M_POLC_LS1_P_MIX(x) (((x)<<12) & PHY_M_POLC_LS1M_MSK)
+#define PHY_M_POLC_IS0_P_MIX(x) (((x)<<8) & PHY_M_POLC_IS0M_MSK)
+#define PHY_M_POLC_LOS_CTRL(x) (((x)<<6) & PHY_M_POLC_LOS_MSK)
+#define PHY_M_POLC_INIT_CTRL(x) (((x)<<4) & PHY_M_POLC_INIT_MSK)
+#define PHY_M_POLC_STA1_CTRL(x) (((x)<<2) & PHY_M_POLC_STA1_MSK)
+#define PHY_M_POLC_STA0_CTRL(x) (((x)<<0) & PHY_M_POLC_STA0_MSK)
+
+enum {
+ PULS_NO_STR = 0,/* no pulse stretching */
+ PULS_21MS = 1,/* 21 ms to 42 ms */
+ PULS_42MS = 2,/* 42 ms to 84 ms */
+ PULS_84MS = 3,/* 84 ms to 170 ms */
+ PULS_170MS = 4,/* 170 ms to 340 ms */
+ PULS_340MS = 5,/* 340 ms to 670 ms */
+ PULS_670MS = 6,/* 670 ms to 1.3 s */
+ PULS_1300MS = 7,/* 1.3 s to 2.7 s */
+};
+
+#define PHY_M_LED_BLINK_RT(x) (((x)<<8) & PHY_M_LEDC_BL_R_MSK)
+
+enum {
+ BLINK_42MS = 0,/* 42 ms */
+ BLINK_84MS = 1,/* 84 ms */
+ BLINK_170MS = 2,/* 170 ms */
+ BLINK_340MS = 3,/* 340 ms */
+ BLINK_670MS = 4,/* 670 ms */
+};
+
+/***** PHY_MARV_LED_OVER 16 bit r/w Manual LED Override Reg *****/
+#define PHY_M_LED_MO_SGMII(x) ((x)<<14) /* Bit 15..14: SGMII AN Timer */
+ /* Bit 13..12: reserved */
+#define PHY_M_LED_MO_DUP(x) ((x)<<10) /* Bit 11..10: Duplex */
+#define PHY_M_LED_MO_10(x) ((x)<<8) /* Bit 9.. 8: Link 10 */
+#define PHY_M_LED_MO_100(x) ((x)<<6) /* Bit 7.. 6: Link 100 */
+#define PHY_M_LED_MO_1000(x) ((x)<<4) /* Bit 5.. 4: Link 1000 */
+#define PHY_M_LED_MO_RX(x) ((x)<<2) /* Bit 3.. 2: Rx */
+#define PHY_M_LED_MO_TX(x) ((x)<<0) /* Bit 1.. 0: Tx */
+
+enum {
+ MO_LED_NORM = 0,
+ MO_LED_BLINK = 1,
+ MO_LED_OFF = 2,
+ MO_LED_ON = 3,
+};
+
+/***** PHY_MARV_EXT_CTRL_2 16 bit r/w Ext. PHY Specific Ctrl 2 *****/
+enum {
+ PHY_M_EC2_FI_IMPED = 1<<6, /* Fiber Input Impedance */
+ PHY_M_EC2_FO_IMPED = 1<<5, /* Fiber Output Impedance */
+ PHY_M_EC2_FO_M_CLK = 1<<4, /* Fiber Mode Clock Enable */
+ PHY_M_EC2_FO_BOOST = 1<<3, /* Fiber Output Boost */
+ PHY_M_EC2_FO_AM_MSK = 7,/* Bit 2.. 0: Fiber Output Amplitude */
+};
+
+/***** PHY_MARV_EXT_P_STAT 16 bit r/w Ext. PHY Specific Status *****/
+enum {
+ PHY_M_FC_AUTO_SEL = 1<<15, /* Fiber/Copper Auto Sel. Dis. */
+ PHY_M_FC_AN_REG_ACC = 1<<14, /* Fiber/Copper AN Reg. Access */
+ PHY_M_FC_RESOLUTION = 1<<13, /* Fiber/Copper Resolution */
+ PHY_M_SER_IF_AN_BP = 1<<12, /* Ser. IF AN Bypass Enable */
+ PHY_M_SER_IF_BP_ST = 1<<11, /* Ser. IF AN Bypass Status */
+ PHY_M_IRQ_POLARITY = 1<<10, /* IRQ polarity */
+ PHY_M_DIS_AUT_MED = 1<<9, /* Disable Aut. Medium Reg. Selection */
+ /* (88E1111 only) */
+
+ PHY_M_UNDOC1 = 1<<7, /* undocumented bit !! */
+ PHY_M_DTE_POW_STAT = 1<<4, /* DTE Power Status (88E1111 only) */
+ PHY_M_MODE_MASK = 0xf, /* Bit 3.. 0: copy of HWCFG MODE[3:0] */
+};
+
+/* for 10/100 Fast Ethernet PHY (88E3082 only) */
+/***** PHY_MARV_FE_LED_PAR 16 bit r/w LED Parallel Select Reg. *****/
+ /* Bit 15..12: reserved (used internally) */
+enum {
+ PHY_M_FELP_LED2_MSK = 0xf<<8, /* Bit 11.. 8: LED2 Mask (LINK) */
+ PHY_M_FELP_LED1_MSK = 0xf<<4, /* Bit 7.. 4: LED1 Mask (ACT) */
+ PHY_M_FELP_LED0_MSK = 0xf, /* Bit 3.. 0: LED0 Mask (SPEED) */
+};
+
+#define PHY_M_FELP_LED2_CTRL(x) (((x)<<8) & PHY_M_FELP_LED2_MSK)
+#define PHY_M_FELP_LED1_CTRL(x) (((x)<<4) & PHY_M_FELP_LED1_MSK)
+#define PHY_M_FELP_LED0_CTRL(x) (((x)<<0) & PHY_M_FELP_LED0_MSK)
+
+enum {
+ LED_PAR_CTRL_COLX = 0x00,
+ LED_PAR_CTRL_ERROR = 0x01,
+ LED_PAR_CTRL_DUPLEX = 0x02,
+ LED_PAR_CTRL_DP_COL = 0x03,
+ LED_PAR_CTRL_SPEED = 0x04,
+ LED_PAR_CTRL_LINK = 0x05,
+ LED_PAR_CTRL_TX = 0x06,
+ LED_PAR_CTRL_RX = 0x07,
+ LED_PAR_CTRL_ACT = 0x08,
+ LED_PAR_CTRL_LNK_RX = 0x09,
+ LED_PAR_CTRL_LNK_AC = 0x0a,
+ LED_PAR_CTRL_ACT_BL = 0x0b,
+ LED_PAR_CTRL_TX_BL = 0x0c,
+ LED_PAR_CTRL_RX_BL = 0x0d,
+ LED_PAR_CTRL_COL_BL = 0x0e,
+ LED_PAR_CTRL_INACT = 0x0f
+};
+
+/*****,PHY_MARV_FE_SPEC_2 16 bit r/w Specific Control Reg. 2 *****/
+enum {
+ PHY_M_FESC_DIS_WAIT = 1<<2, /* Disable TDR Waiting Period */
+ PHY_M_FESC_ENA_MCLK = 1<<1, /* Enable MAC Rx Clock in sleep mode */
+ PHY_M_FESC_SEL_CL_A = 1<<0, /* Select Class A driver (100B-TX) */
+};
+
+/* for Yukon-2 Gigabit Ethernet PHY (88E1112 only) */
+/***** PHY_MARV_PHY_CTRL (page 2) 16 bit r/w MAC Specific Ctrl *****/
+enum {
+ PHY_M_MAC_MD_MSK = 7<<7, /* Bit 9.. 7: Mode Select Mask */
+ PHY_M_MAC_MD_AUTO = 3,/* Auto Copper/1000Base-X */
+ PHY_M_MAC_MD_COPPER = 5,/* Copper only */
+ PHY_M_MAC_MD_1000BX = 7,/* 1000Base-X only */
+};
+#define PHY_M_MAC_MODE_SEL(x) (((x)<<7) & PHY_M_MAC_MD_MSK)
+
+/***** PHY_MARV_PHY_CTRL (page 3) 16 bit r/w LED Control Reg. *****/
+enum {
+ PHY_M_LEDC_LOS_MSK = 0xf<<12,/* Bit 15..12: LOS LED Ctrl. Mask */
+ PHY_M_LEDC_INIT_MSK = 0xf<<8, /* Bit 11.. 8: INIT LED Ctrl. Mask */
+ PHY_M_LEDC_STA1_MSK = 0xf<<4,/* Bit 7.. 4: STAT1 LED Ctrl. Mask */
+ PHY_M_LEDC_STA0_MSK = 0xf, /* Bit 3.. 0: STAT0 LED Ctrl. Mask */
+};
+
+#define PHY_M_LEDC_LOS_CTRL(x) (((x)<<12) & PHY_M_LEDC_LOS_MSK)
+#define PHY_M_LEDC_INIT_CTRL(x) (((x)<<8) & PHY_M_LEDC_INIT_MSK)
+#define PHY_M_LEDC_STA1_CTRL(x) (((x)<<4) & PHY_M_LEDC_STA1_MSK)
+#define PHY_M_LEDC_STA0_CTRL(x) (((x)<<0) & PHY_M_LEDC_STA0_MSK)
+
+/* GMAC registers */
+/* Port Registers */
+enum {
+ GM_GP_STAT = 0x0000, /* 16 bit r/o General Purpose Status */
+ GM_GP_CTRL = 0x0004, /* 16 bit r/w General Purpose Control */
+ GM_TX_CTRL = 0x0008, /* 16 bit r/w Transmit Control Reg. */
+ GM_RX_CTRL = 0x000c, /* 16 bit r/w Receive Control Reg. */
+ GM_TX_FLOW_CTRL = 0x0010, /* 16 bit r/w Transmit Flow-Control */
+ GM_TX_PARAM = 0x0014, /* 16 bit r/w Transmit Parameter Reg. */
+ GM_SERIAL_MODE = 0x0018, /* 16 bit r/w Serial Mode Register */
+/* Source Address Registers */
+ GM_SRC_ADDR_1L = 0x001c, /* 16 bit r/w Source Address 1 (low) */
+ GM_SRC_ADDR_1M = 0x0020, /* 16 bit r/w Source Address 1 (middle) */
+ GM_SRC_ADDR_1H = 0x0024, /* 16 bit r/w Source Address 1 (high) */
+ GM_SRC_ADDR_2L = 0x0028, /* 16 bit r/w Source Address 2 (low) */
+ GM_SRC_ADDR_2M = 0x002c, /* 16 bit r/w Source Address 2 (middle) */
+ GM_SRC_ADDR_2H = 0x0030, /* 16 bit r/w Source Address 2 (high) */
+
+/* Multicast Address Hash Registers */
+ GM_MC_ADDR_H1 = 0x0034, /* 16 bit r/w Multicast Address Hash 1 */
+ GM_MC_ADDR_H2 = 0x0038, /* 16 bit r/w Multicast Address Hash 2 */
+ GM_MC_ADDR_H3 = 0x003c, /* 16 bit r/w Multicast Address Hash 3 */
+ GM_MC_ADDR_H4 = 0x0040, /* 16 bit r/w Multicast Address Hash 4 */
+
+/* Interrupt Source Registers */
+ GM_TX_IRQ_SRC = 0x0044, /* 16 bit r/o Tx Overflow IRQ Source */
+ GM_RX_IRQ_SRC = 0x0048, /* 16 bit r/o Rx Overflow IRQ Source */
+ GM_TR_IRQ_SRC = 0x004c, /* 16 bit r/o Tx/Rx Over. IRQ Source */
+
+/* Interrupt Mask Registers */
+ GM_TX_IRQ_MSK = 0x0050, /* 16 bit r/w Tx Overflow IRQ Mask */
+ GM_RX_IRQ_MSK = 0x0054, /* 16 bit r/w Rx Overflow IRQ Mask */
+ GM_TR_IRQ_MSK = 0x0058, /* 16 bit r/w Tx/Rx Over. IRQ Mask */
+
+/* Serial Management Interface (SMI) Registers */
+ GM_SMI_CTRL = 0x0080, /* 16 bit r/w SMI Control Register */
+ GM_SMI_DATA = 0x0084, /* 16 bit r/w SMI Data Register */
+ GM_PHY_ADDR = 0x0088, /* 16 bit r/w GPHY Address Register */
+};
+
+/* MIB Counters */
+#define GM_MIB_CNT_BASE 0x0100 /* Base Address of MIB Counters */
+#define GM_MIB_CNT_SIZE 44 /* Number of MIB Counters */
+
+/*
+ * MIB Counters base address definitions (low word) -
+ * use offset 4 for access to high word (32 bit r/o)
+ */
+enum {
+ GM_RXF_UC_OK = GM_MIB_CNT_BASE + 0, /* Unicast Frames Received OK */
+ GM_RXF_BC_OK = GM_MIB_CNT_BASE + 8, /* Broadcast Frames Received OK */
+ GM_RXF_MPAUSE = GM_MIB_CNT_BASE + 16, /* Pause MAC Ctrl Frames Received */
+ GM_RXF_MC_OK = GM_MIB_CNT_BASE + 24, /* Multicast Frames Received OK */
+ GM_RXF_FCS_ERR = GM_MIB_CNT_BASE + 32, /* Rx Frame Check Seq. Error */
+ /* GM_MIB_CNT_BASE + 40: reserved */
+ GM_RXO_OK_LO = GM_MIB_CNT_BASE + 48, /* Octets Received OK Low */
+ GM_RXO_OK_HI = GM_MIB_CNT_BASE + 56, /* Octets Received OK High */
+ GM_RXO_ERR_LO = GM_MIB_CNT_BASE + 64, /* Octets Received Invalid Low */
+ GM_RXO_ERR_HI = GM_MIB_CNT_BASE + 72, /* Octets Received Invalid High */
+ GM_RXF_SHT = GM_MIB_CNT_BASE + 80, /* Frames <64 Byte Received OK */
+ GM_RXE_FRAG = GM_MIB_CNT_BASE + 88, /* Frames <64 Byte Received with FCS Err */
+ GM_RXF_64B = GM_MIB_CNT_BASE + 96, /* 64 Byte Rx Frame */
+ GM_RXF_127B = GM_MIB_CNT_BASE + 104, /* 65-127 Byte Rx Frame */
+ GM_RXF_255B = GM_MIB_CNT_BASE + 112, /* 128-255 Byte Rx Frame */
+ GM_RXF_511B = GM_MIB_CNT_BASE + 120, /* 256-511 Byte Rx Frame */
+ GM_RXF_1023B = GM_MIB_CNT_BASE + 128, /* 512-1023 Byte Rx Frame */
+ GM_RXF_1518B = GM_MIB_CNT_BASE + 136, /* 1024-1518 Byte Rx Frame */
+ GM_RXF_MAX_SZ = GM_MIB_CNT_BASE + 144, /* 1519-MaxSize Byte Rx Frame */
+ GM_RXF_LNG_ERR = GM_MIB_CNT_BASE + 152, /* Rx Frame too Long Error */
+ GM_RXF_JAB_PKT = GM_MIB_CNT_BASE + 160, /* Rx Jabber Packet Frame */
+ /* GM_MIB_CNT_BASE + 168: reserved */
+ GM_RXE_FIFO_OV = GM_MIB_CNT_BASE + 176, /* Rx FIFO overflow Event */
+ /* GM_MIB_CNT_BASE + 184: reserved */
+ GM_TXF_UC_OK = GM_MIB_CNT_BASE + 192, /* Unicast Frames Xmitted OK */
+ GM_TXF_BC_OK = GM_MIB_CNT_BASE + 200, /* Broadcast Frames Xmitted OK */
+ GM_TXF_MPAUSE = GM_MIB_CNT_BASE + 208, /* Pause MAC Ctrl Frames Xmitted */
+ GM_TXF_MC_OK = GM_MIB_CNT_BASE + 216, /* Multicast Frames Xmitted OK */
+ GM_TXO_OK_LO = GM_MIB_CNT_BASE + 224, /* Octets Transmitted OK Low */
+ GM_TXO_OK_HI = GM_MIB_CNT_BASE + 232, /* Octets Transmitted OK High */
+ GM_TXF_64B = GM_MIB_CNT_BASE + 240, /* 64 Byte Tx Frame */
+ GM_TXF_127B = GM_MIB_CNT_BASE + 248, /* 65-127 Byte Tx Frame */
+ GM_TXF_255B = GM_MIB_CNT_BASE + 256, /* 128-255 Byte Tx Frame */
+ GM_TXF_511B = GM_MIB_CNT_BASE + 264, /* 256-511 Byte Tx Frame */
+ GM_TXF_1023B = GM_MIB_CNT_BASE + 272, /* 512-1023 Byte Tx Frame */
+ GM_TXF_1518B = GM_MIB_CNT_BASE + 280, /* 1024-1518 Byte Tx Frame */
+ GM_TXF_MAX_SZ = GM_MIB_CNT_BASE + 288, /* 1519-MaxSize Byte Tx Frame */
+
+ GM_TXF_COL = GM_MIB_CNT_BASE + 304, /* Tx Collision */
+ GM_TXF_LAT_COL = GM_MIB_CNT_BASE + 312, /* Tx Late Collision */
+ GM_TXF_ABO_COL = GM_MIB_CNT_BASE + 320, /* Tx aborted due to Exces. Col. */
+ GM_TXF_MUL_COL = GM_MIB_CNT_BASE + 328, /* Tx Multiple Collision */
+ GM_TXF_SNG_COL = GM_MIB_CNT_BASE + 336, /* Tx Single Collision */
+ GM_TXE_FIFO_UR = GM_MIB_CNT_BASE + 344, /* Tx FIFO Underrun Event */
+};
+
+/* GMAC Bit Definitions */
+/* GM_GP_STAT 16 bit r/o General Purpose Status Register */
+enum {
+ GM_GPSR_SPEED = 1<<15, /* Bit 15: Port Speed (1 = 100 Mbps) */
+ GM_GPSR_DUPLEX = 1<<14, /* Bit 14: Duplex Mode (1 = Full) */
+ GM_GPSR_FC_TX_DIS = 1<<13, /* Bit 13: Tx Flow-Control Mode Disabled */
+ GM_GPSR_LINK_UP = 1<<12, /* Bit 12: Link Up Status */
+ GM_GPSR_PAUSE = 1<<11, /* Bit 11: Pause State */
+ GM_GPSR_TX_ACTIVE = 1<<10, /* Bit 10: Tx in Progress */
+ GM_GPSR_EXC_COL = 1<<9, /* Bit 9: Excessive Collisions Occured */
+ GM_GPSR_LAT_COL = 1<<8, /* Bit 8: Late Collisions Occured */
+
+ GM_GPSR_PHY_ST_CH = 1<<5, /* Bit 5: PHY Status Change */
+ GM_GPSR_GIG_SPEED = 1<<4, /* Bit 4: Gigabit Speed (1 = 1000 Mbps) */
+ GM_GPSR_PART_MODE = 1<<3, /* Bit 3: Partition mode */
+ GM_GPSR_FC_RX_DIS = 1<<2, /* Bit 2: Rx Flow-Control Mode Disabled */
+ GM_GPSR_PROM_EN = 1<<1, /* Bit 1: Promiscuous Mode Enabled */
+};
+
+/* GM_GP_CTRL 16 bit r/w General Purpose Control Register */
+enum {
+ GM_GPCR_PROM_ENA = 1<<14, /* Bit 14: Enable Promiscuous Mode */
+ GM_GPCR_FC_TX_DIS = 1<<13, /* Bit 13: Disable Tx Flow-Control Mode */
+ GM_GPCR_TX_ENA = 1<<12, /* Bit 12: Enable Transmit */
+ GM_GPCR_RX_ENA = 1<<11, /* Bit 11: Enable Receive */
+ GM_GPCR_BURST_ENA = 1<<10, /* Bit 10: Enable Burst Mode */
+ GM_GPCR_LOOP_ENA = 1<<9, /* Bit 9: Enable MAC Loopback Mode */
+ GM_GPCR_PART_ENA = 1<<8, /* Bit 8: Enable Partition Mode */
+ GM_GPCR_GIGS_ENA = 1<<7, /* Bit 7: Gigabit Speed (1000 Mbps) */
+ GM_GPCR_FL_PASS = 1<<6, /* Bit 6: Force Link Pass */
+ GM_GPCR_DUP_FULL = 1<<5, /* Bit 5: Full Duplex Mode */
+ GM_GPCR_FC_RX_DIS = 1<<4, /* Bit 4: Disable Rx Flow-Control Mode */
+ GM_GPCR_SPEED_100 = 1<<3, /* Bit 3: Port Speed 100 Mbps */
+ GM_GPCR_AU_DUP_DIS = 1<<2, /* Bit 2: Disable Auto-Update Duplex */
+ GM_GPCR_AU_FCT_DIS = 1<<1, /* Bit 1: Disable Auto-Update Flow-C. */
+ GM_GPCR_AU_SPD_DIS = 1<<0, /* Bit 0: Disable Auto-Update Speed */
+};
+
+#define GM_GPCR_SPEED_1000 (GM_GPCR_GIGS_ENA | GM_GPCR_SPEED_100)
+#define GM_GPCR_AU_ALL_DIS (GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS|GM_GPCR_AU_SPD_DIS)
+
+/* GM_TX_CTRL 16 bit r/w Transmit Control Register */
+enum {
+ GM_TXCR_FORCE_JAM = 1<<15, /* Bit 15: Force Jam / Flow-Control */
+ GM_TXCR_CRC_DIS = 1<<14, /* Bit 14: Disable insertion of CRC */
+ GM_TXCR_PAD_DIS = 1<<13, /* Bit 13: Disable padding of packets */
+ GM_TXCR_COL_THR_MSK = 1<<10, /* Bit 12..10: Collision Threshold */
+};
+
+#define TX_COL_THR(x) (((x)<<10) & GM_TXCR_COL_THR_MSK)
+#define TX_COL_DEF 0x04
+
+/* GM_RX_CTRL 16 bit r/w Receive Control Register */
+enum {
+ GM_RXCR_UCF_ENA = 1<<15, /* Bit 15: Enable Unicast filtering */
+ GM_RXCR_MCF_ENA = 1<<14, /* Bit 14: Enable Multicast filtering */
+ GM_RXCR_CRC_DIS = 1<<13, /* Bit 13: Remove 4-byte CRC */
+ GM_RXCR_PASS_FC = 1<<12, /* Bit 12: Pass FC packets to FIFO */
+};
+
+/* GM_TX_PARAM 16 bit r/w Transmit Parameter Register */
+enum {
+ GM_TXPA_JAMLEN_MSK = 0x03<<14, /* Bit 15..14: Jam Length */
+ GM_TXPA_JAMIPG_MSK = 0x1f<<9, /* Bit 13..9: Jam IPG */
+ GM_TXPA_JAMDAT_MSK = 0x1f<<4, /* Bit 8..4: IPG Jam to Data */
+ GM_TXPA_BO_LIM_MSK = 0x0f, /* Bit 3.. 0: Backoff Limit Mask */
+
+ TX_JAM_LEN_DEF = 0x03,
+ TX_JAM_IPG_DEF = 0x0b,
+ TX_IPG_JAM_DEF = 0x1c,
+ TX_BOF_LIM_DEF = 0x04,
+};
+
+#define TX_JAM_LEN_VAL(x) (((x)<<14) & GM_TXPA_JAMLEN_MSK)
+#define TX_JAM_IPG_VAL(x) (((x)<<9) & GM_TXPA_JAMIPG_MSK)
+#define TX_IPG_JAM_DATA(x) (((x)<<4) & GM_TXPA_JAMDAT_MSK)
+#define TX_BACK_OFF_LIM(x) ((x) & GM_TXPA_BO_LIM_MSK)
+
+
+/* GM_SERIAL_MODE 16 bit r/w Serial Mode Register */
+enum {
+ GM_SMOD_DATABL_MSK = 0x1f<<11, /* Bit 15..11: Data Blinder (r/o) */
+ GM_SMOD_LIMIT_4 = 1<<10, /* Bit 10: 4 consecutive Tx trials */
+ GM_SMOD_VLAN_ENA = 1<<9, /* Bit 9: Enable VLAN (Max. Frame Len) */
+ GM_SMOD_JUMBO_ENA = 1<<8, /* Bit 8: Enable Jumbo (Max. Frame Len) */
+ GM_SMOD_IPG_MSK = 0x1f /* Bit 4..0: Inter-Packet Gap (IPG) */
+};
+
+#define DATA_BLIND_VAL(x) (((x)<<11) & GM_SMOD_DATABL_MSK)
+#define DATA_BLIND_DEF 0x04
+
+#define IPG_DATA_VAL(x) (x & GM_SMOD_IPG_MSK)
+#define IPG_DATA_DEF 0x1e
+
+/* GM_SMI_CTRL 16 bit r/w SMI Control Register */
+enum {
+ GM_SMI_CT_PHY_A_MSK = 0x1f<<11,/* Bit 15..11: PHY Device Address */
+ GM_SMI_CT_REG_A_MSK = 0x1f<<6,/* Bit 10.. 6: PHY Register Address */
+ GM_SMI_CT_OP_RD = 1<<5, /* Bit 5: OpCode Read (0=Write)*/
+ GM_SMI_CT_RD_VAL = 1<<4, /* Bit 4: Read Valid (Read completed) */
+ GM_SMI_CT_BUSY = 1<<3, /* Bit 3: Busy (Operation in progress) */
+};
+
+#define GM_SMI_CT_PHY_AD(x) (((x)<<11) & GM_SMI_CT_PHY_A_MSK)
+#define GM_SMI_CT_REG_AD(x) (((x)<<6) & GM_SMI_CT_REG_A_MSK)
+
+/* GM_PHY_ADDR 16 bit r/w GPHY Address Register */
+enum {
+ GM_PAR_MIB_CLR = 1<<5, /* Bit 5: Set MIB Clear Counter Mode */
+ GM_PAR_MIB_TST = 1<<4, /* Bit 4: MIB Load Counter (Test Mode) */
+};
+
+/* Receive Frame Status Encoding */
+enum {
+ GMR_FS_LEN = 0xffff<<16, /* Bit 31..16: Rx Frame Length */
+ GMR_FS_VLAN = 1<<13, /* VLAN Packet */
+ GMR_FS_JABBER = 1<<12, /* Jabber Packet */
+ GMR_FS_UN_SIZE = 1<<11, /* Undersize Packet */
+ GMR_FS_MC = 1<<10, /* Multicast Packet */
+ GMR_FS_BC = 1<<9, /* Broadcast Packet */
+ GMR_FS_RX_OK = 1<<8, /* Receive OK (Good Packet) */
+ GMR_FS_GOOD_FC = 1<<7, /* Good Flow-Control Packet */
+ GMR_FS_BAD_FC = 1<<6, /* Bad Flow-Control Packet */
+ GMR_FS_MII_ERR = 1<<5, /* MII Error */
+ GMR_FS_LONG_ERR = 1<<4, /* Too Long Packet */
+ GMR_FS_FRAGMENT = 1<<3, /* Fragment */
+
+ GMR_FS_CRC_ERR = 1<<1, /* CRC Error */
+ GMR_FS_RX_FF_OV = 1<<0, /* Rx FIFO Overflow */
+
+ GMR_FS_ANY_ERR = GMR_FS_RX_FF_OV | GMR_FS_CRC_ERR |
+ GMR_FS_FRAGMENT | GMR_FS_LONG_ERR |
+ GMR_FS_MII_ERR | GMR_FS_BAD_FC | GMR_FS_GOOD_FC |
+ GMR_FS_UN_SIZE | GMR_FS_JABBER,
+};
+
+/* RX_GMF_CTRL_T 32 bit Rx GMAC FIFO Control/Test */
+enum {
+ RX_TRUNC_ON = 1<<27, /* enable packet truncation */
+ RX_TRUNC_OFF = 1<<26, /* disable packet truncation */
+ RX_VLAN_STRIP_ON = 1<<25, /* enable VLAN stripping */
+ RX_VLAN_STRIP_OFF = 1<<24, /* disable VLAN stripping */
+
+ GMF_WP_TST_ON = 1<<14, /* Write Pointer Test On */
+ GMF_WP_TST_OFF = 1<<13, /* Write Pointer Test Off */
+ GMF_WP_STEP = 1<<12, /* Write Pointer Step/Increment */
+
+ GMF_RP_TST_ON = 1<<10, /* Read Pointer Test On */
+ GMF_RP_TST_OFF = 1<<9, /* Read Pointer Test Off */
+ GMF_RP_STEP = 1<<8, /* Read Pointer Step/Increment */
+ GMF_RX_F_FL_ON = 1<<7, /* Rx FIFO Flush Mode On */
+ GMF_RX_F_FL_OFF = 1<<6, /* Rx FIFO Flush Mode Off */
+ GMF_CLI_RX_FO = 1<<5, /* Clear IRQ Rx FIFO Overrun */
+ GMF_CLI_RX_C = 1<<4, /* Clear IRQ Rx Frame Complete */
+
+ GMF_OPER_ON = 1<<3, /* Operational Mode On */
+ GMF_OPER_OFF = 1<<2, /* Operational Mode Off */
+ GMF_RST_CLR = 1<<1, /* Clear GMAC FIFO Reset */
+ GMF_RST_SET = 1<<0, /* Set GMAC FIFO Reset */
+
+ RX_GMF_FL_THR_DEF = 0xa, /* flush threshold (default) */
+
+ GMF_RX_CTRL_DEF = GMF_OPER_ON | GMF_RX_F_FL_ON,
+};
+
+
+/* TX_GMF_CTRL_T 32 bit Tx GMAC FIFO Control/Test */
+enum {
+ TX_VLAN_TAG_ON = 1<<25,/* enable VLAN tagging */
+ TX_VLAN_TAG_OFF = 1<<24,/* disable VLAN tagging */
+
+ GMF_WSP_TST_ON = 1<<18,/* Write Shadow Pointer Test On */
+ GMF_WSP_TST_OFF = 1<<17,/* Write Shadow Pointer Test Off */
+ GMF_WSP_STEP = 1<<16,/* Write Shadow Pointer Step/Increment */
+
+ GMF_CLI_TX_FU = 1<<6, /* Clear IRQ Tx FIFO Underrun */
+ GMF_CLI_TX_FC = 1<<5, /* Clear IRQ Tx Frame Complete */
+ GMF_CLI_TX_PE = 1<<4, /* Clear IRQ Tx Parity Error */
+};
+
+/* GMAC_TI_ST_CTRL 8 bit Time Stamp Timer Ctrl Reg (YUKON only) */
+enum {
+ GMT_ST_START = 1<<2, /* Start Time Stamp Timer */
+ GMT_ST_STOP = 1<<1, /* Stop Time Stamp Timer */
+ GMT_ST_CLR_IRQ = 1<<0, /* Clear Time Stamp Timer IRQ */
+};
+
+/* B28_Y2_ASF_STAT_CMD 32 bit ASF Status and Command Reg */
+enum {
+ Y2_ASF_OS_PRES = 1<<4, /* ASF operation system present */
+ Y2_ASF_RESET = 1<<3, /* ASF system in reset state */
+ Y2_ASF_RUNNING = 1<<2, /* ASF system operational */
+ Y2_ASF_CLR_HSTI = 1<<1, /* Clear ASF IRQ */
+ Y2_ASF_IRQ = 1<<0, /* Issue an IRQ to ASF system */
+
+ Y2_ASF_UC_STATE = 3<<2, /* ASF uC State */
+ Y2_ASF_CLK_HALT = 0, /* ASF system clock stopped */
+};
+
+/* B28_Y2_ASF_HOST_COM 32 bit ASF Host Communication Reg */
+enum {
+ Y2_ASF_CLR_ASFI = 1<<1, /* Clear host IRQ */
+ Y2_ASF_HOST_IRQ = 1<<0, /* Issue an IRQ to HOST system */
+};
+
+/* STAT_CTRL 32 bit Status BMU control register (Yukon-2 only) */
+enum {
+ SC_STAT_CLR_IRQ = 1<<4, /* Status Burst IRQ clear */
+ SC_STAT_OP_ON = 1<<3, /* Operational Mode On */
+ SC_STAT_OP_OFF = 1<<2, /* Operational Mode Off */
+ SC_STAT_RST_CLR = 1<<1, /* Clear Status Unit Reset (Enable) */
+ SC_STAT_RST_SET = 1<<0, /* Set Status Unit Reset */
+};
+
+/* GMAC_CTRL 32 bit GMAC Control Reg (YUKON only) */
+enum {
+ GMC_H_BURST_ON = 1<<7, /* Half Duplex Burst Mode On */
+ GMC_H_BURST_OFF = 1<<6, /* Half Duplex Burst Mode Off */
+ GMC_F_LOOPB_ON = 1<<5, /* FIFO Loopback On */
+ GMC_F_LOOPB_OFF = 1<<4, /* FIFO Loopback Off */
+ GMC_PAUSE_ON = 1<<3, /* Pause On */
+ GMC_PAUSE_OFF = 1<<2, /* Pause Off */
+ GMC_RST_CLR = 1<<1, /* Clear GMAC Reset */
+ GMC_RST_SET = 1<<0, /* Set GMAC Reset */
+};
+
+/* GPHY_CTRL 32 bit GPHY Control Reg (YUKON only) */
+enum {
+ GPC_SEL_BDT = 1<<28, /* Select Bi-Dir. Transfer for MDC/MDIO */
+ GPC_INT_POL_HI = 1<<27, /* IRQ Polarity is Active HIGH */
+ GPC_75_OHM = 1<<26, /* Use 75 Ohm Termination instead of 50 */
+ GPC_DIS_FC = 1<<25, /* Disable Automatic Fiber/Copper Detection */
+ GPC_DIS_SLEEP = 1<<24, /* Disable Energy Detect */
+ GPC_HWCFG_M_3 = 1<<23, /* HWCFG_MODE[3] */
+ GPC_HWCFG_M_2 = 1<<22, /* HWCFG_MODE[2] */
+ GPC_HWCFG_M_1 = 1<<21, /* HWCFG_MODE[1] */
+ GPC_HWCFG_M_0 = 1<<20, /* HWCFG_MODE[0] */
+ GPC_ANEG_0 = 1<<19, /* ANEG[0] */
+ GPC_ENA_XC = 1<<18, /* Enable MDI crossover */
+ GPC_DIS_125 = 1<<17, /* Disable 125 MHz clock */
+ GPC_ANEG_3 = 1<<16, /* ANEG[3] */
+ GPC_ANEG_2 = 1<<15, /* ANEG[2] */
+ GPC_ANEG_1 = 1<<14, /* ANEG[1] */
+ GPC_ENA_PAUSE = 1<<13, /* Enable Pause (SYM_OR_REM) */
+ GPC_PHYADDR_4 = 1<<12, /* Bit 4 of Phy Addr */
+ GPC_PHYADDR_3 = 1<<11, /* Bit 3 of Phy Addr */
+ GPC_PHYADDR_2 = 1<<10, /* Bit 2 of Phy Addr */
+ GPC_PHYADDR_1 = 1<<9, /* Bit 1 of Phy Addr */
+ GPC_PHYADDR_0 = 1<<8, /* Bit 0 of Phy Addr */
+ /* Bits 7..2: reserved */
+ GPC_RST_CLR = 1<<1, /* Clear GPHY Reset */
+ GPC_RST_SET = 1<<0, /* Set GPHY Reset */
+};
+
+/* GMAC_IRQ_SRC 8 bit GMAC Interrupt Source Reg (YUKON only) */
+/* GMAC_IRQ_MSK 8 bit GMAC Interrupt Mask Reg (YUKON only) */
+enum {
+ GM_IS_TX_CO_OV = 1<<5, /* Transmit Counter Overflow IRQ */
+ GM_IS_RX_CO_OV = 1<<4, /* Receive Counter Overflow IRQ */
+ GM_IS_TX_FF_UR = 1<<3, /* Transmit FIFO Underrun */
+ GM_IS_TX_COMPL = 1<<2, /* Frame Transmission Complete */
+ GM_IS_RX_FF_OR = 1<<1, /* Receive FIFO Overrun */
+ GM_IS_RX_COMPL = 1<<0, /* Frame Reception Complete */
+
+#define GMAC_DEF_MSK GM_IS_TX_FF_UR
+
+/* GMAC_LINK_CTRL 16 bit GMAC Link Control Reg (YUKON only) */
+ /* Bits 15.. 2: reserved */
+ GMLC_RST_CLR = 1<<1, /* Clear GMAC Link Reset */
+ GMLC_RST_SET = 1<<0, /* Set GMAC Link Reset */
+
+
+/* WOL_CTRL_STAT 16 bit WOL Control/Status Reg */
+ WOL_CTL_LINK_CHG_OCC = 1<<15,
+ WOL_CTL_MAGIC_PKT_OCC = 1<<14,
+ WOL_CTL_PATTERN_OCC = 1<<13,
+ WOL_CTL_CLEAR_RESULT = 1<<12,
+ WOL_CTL_ENA_PME_ON_LINK_CHG = 1<<11,
+ WOL_CTL_DIS_PME_ON_LINK_CHG = 1<<10,
+ WOL_CTL_ENA_PME_ON_MAGIC_PKT = 1<<9,
+ WOL_CTL_DIS_PME_ON_MAGIC_PKT = 1<<8,
+ WOL_CTL_ENA_PME_ON_PATTERN = 1<<7,
+ WOL_CTL_DIS_PME_ON_PATTERN = 1<<6,
+ WOL_CTL_ENA_LINK_CHG_UNIT = 1<<5,
+ WOL_CTL_DIS_LINK_CHG_UNIT = 1<<4,
+ WOL_CTL_ENA_MAGIC_PKT_UNIT = 1<<3,
+ WOL_CTL_DIS_MAGIC_PKT_UNIT = 1<<2,
+ WOL_CTL_ENA_PATTERN_UNIT = 1<<1,
+ WOL_CTL_DIS_PATTERN_UNIT = 1<<0,
+};
+
+#define WOL_CTL_DEFAULT \
+ (WOL_CTL_DIS_PME_ON_LINK_CHG | \
+ WOL_CTL_DIS_PME_ON_PATTERN | \
+ WOL_CTL_DIS_PME_ON_MAGIC_PKT | \
+ WOL_CTL_DIS_LINK_CHG_UNIT | \
+ WOL_CTL_DIS_PATTERN_UNIT | \
+ WOL_CTL_DIS_MAGIC_PKT_UNIT)
+
+/* WOL_MATCH_CTL 8 bit WOL Match Control Reg */
+#define WOL_CTL_PATT_ENA(x) (1 << (x))
+
+
+/* Control flags */
+enum {
+ UDPTCP = 1<<0,
+ CALSUM = 1<<1,
+ WR_SUM = 1<<2,
+ INIT_SUM= 1<<3,
+ LOCK_SUM= 1<<4,
+ INS_VLAN= 1<<5,
+ FRC_STAT= 1<<6,
+ EOP = 1<<7,
+};
+
+enum {
+ HW_OWNER = 1<<7,
+ OP_TCPWRITE = 0x11,
+ OP_TCPSTART = 0x12,
+ OP_TCPINIT = 0x14,
+ OP_TCPLCK = 0x18,
+ OP_TCPCHKSUM = OP_TCPSTART,
+ OP_TCPIS = OP_TCPINIT | OP_TCPSTART,
+ OP_TCPLW = OP_TCPLCK | OP_TCPWRITE,
+ OP_TCPLSW = OP_TCPLCK | OP_TCPSTART | OP_TCPWRITE,
+ OP_TCPLISW = OP_TCPLCK | OP_TCPINIT | OP_TCPSTART | OP_TCPWRITE,
+
+ OP_ADDR64 = 0x21,
+ OP_VLAN = 0x22,
+ OP_ADDR64VLAN = OP_ADDR64 | OP_VLAN,
+ OP_LRGLEN = 0x24,
+ OP_LRGLENVLAN = OP_LRGLEN | OP_VLAN,
+ OP_BUFFER = 0x40,
+ OP_PACKET = 0x41,
+ OP_LARGESEND = 0x43,
+
+/* YUKON-2 STATUS opcodes defines */
+ OP_RXSTAT = 0x60,
+ OP_RXTIMESTAMP = 0x61,
+ OP_RXVLAN = 0x62,
+ OP_RXCHKS = 0x64,
+ OP_RXCHKSVLAN = OP_RXCHKS | OP_RXVLAN,
+ OP_RXTIMEVLAN = OP_RXTIMESTAMP | OP_RXVLAN,
+ OP_RSS_HASH = 0x65,
+ OP_TXINDEXLE = 0x68,
+};
+
+/* Yukon 2 hardware interface
+ * Not tested on big endian
+ */
+struct sky2_tx_le {
+ union {
+ u32 addr;
+ struct {
+ u16 offset;
+ u16 start;
+ } csum __attribute((packed));
+ struct {
+ u16 size;
+ u16 rsvd;
+ } tso __attribute((packed));
+ } tx;
+ u16 length; /* also vlan tag or checksum start */
+ u8 ctrl;
+ u8 opcode;
+} __attribute((packed));
+
+struct sky2_rx_le {
+ u32 addr;
+ u16 length;
+ u8 ctrl;
+ u8 opcode;
+} __attribute((packed));;
+
+struct sky2_status_le {
+ u32 status; /* also checksum */
+ u16 length; /* also vlan tag */
+ u8 link;
+ u8 opcode;
+} __attribute((packed));
+
+struct ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapaddr;
+ u16 maplen;
+ u16 idx;
+};
+
+struct sky2_port {
+ struct sky2_hw *hw;
+ struct net_device *netdev;
+ unsigned port;
+ u32 msg_enable;
+
+ struct ring_info *tx_ring;
+ struct sky2_tx_le *tx_le;
+ spinlock_t tx_lock;
+ u32 tx_addr64;
+ u16 tx_cons; /* next le to check */
+ u16 tx_prod; /* next le to use */
+ u16 tx_pending;
+ u16 tx_last_put;
+ u16 tx_last_mss;
+
+ struct ring_info *rx_ring;
+ struct sky2_rx_le *rx_le;
+ u32 rx_addr64;
+ u16 rx_next; /* next re to check */
+ u16 rx_put; /* next le index to use */
+ u16 rx_pending;
+ u16 rx_last_put;
+#ifdef SKY2_VLAN_TAG_USED
+ u16 rx_tag;
+ struct vlan_group *vlgrp;
+#endif
+
+ dma_addr_t rx_le_map;
+ dma_addr_t tx_le_map;
+ u32 advertising; /* ADVERTISED_ bits */
+ u16 speed; /* SPEED_1000, SPEED_100, ... */
+ u8 autoneg; /* AUTONEG_ENABLE, AUTONEG_DISABLE */
+ u8 duplex; /* DUPLEX_HALF, DUPLEX_FULL */
+ u8 rx_pause;
+ u8 tx_pause;
+ u8 rx_csum;
+ u8 wol;
+
+ struct tasklet_struct phy_task;
+ struct net_device_stats net_stats;
+};
+
+struct sky2_hw {
+ void __iomem *regs;
+ struct pci_dev *pdev;
+ u32 intr_mask;
+ struct net_device *dev[2];
+
+ int pm_cap;
+ u8 chip_id;
+ u8 chip_rev;
+ u8 copper;
+ u8 ports;
+
+ struct sky2_status_le *st_le;
+ u32 st_idx;
+ dma_addr_t st_dma;
+
+ spinlock_t phy_lock;
+};
+
+/* Register accessor for memory mapped device */
+static inline u32 sky2_read32(const struct sky2_hw *hw, unsigned reg)
+{
+ return readl(hw->regs + reg);
+}
+
+static inline u16 sky2_read16(const struct sky2_hw *hw, unsigned reg)
+{
+ return readw(hw->regs + reg);
+}
+
+static inline u8 sky2_read8(const struct sky2_hw *hw, unsigned reg)
+{
+ return readb(hw->regs + reg);
+}
+
+/* This should probably go away, bus based tweeks suck */
+static inline int is_pciex(const struct sky2_hw *hw)
+{
+ u32 status;
+ pci_read_config_dword(hw->pdev, PCI_DEV_STATUS, &status);
+ return (status & PCI_OS_PCI_X) == 0;
+}
+
+static inline void sky2_write32(const struct sky2_hw *hw, unsigned reg, u32 val)
+{
+ writel(val, hw->regs + reg);
+}
+
+static inline void sky2_write16(const struct sky2_hw *hw, unsigned reg, u16 val)
+{
+ writew(val, hw->regs + reg);
+}
+
+static inline void sky2_write8(const struct sky2_hw *hw, unsigned reg, u8 val)
+{
+ writeb(val, hw->regs + reg);
+}
+
+/* Yukon PHY related registers */
+#define SK_GMAC_REG(port,reg) \
+ (BASE_GMAC_1 + (port) * (BASE_GMAC_2-BASE_GMAC_1) + (reg))
+#define GM_PHY_RETRIES 100
+
+static inline u16 gma_read16(const struct sky2_hw *hw, unsigned port, unsigned reg)
+{
+ return sky2_read16(hw, SK_GMAC_REG(port,reg));
+}
+
+static inline u32 gma_read32(struct sky2_hw *hw, unsigned port, unsigned reg)
+{
+ unsigned base = SK_GMAC_REG(port, reg);
+ return (u32) sky2_read16(hw, base)
+ | (u32) sky2_read16(hw, base+4) << 16;
+}
+
+static inline void gma_write16(const struct sky2_hw *hw, unsigned port, int r, u16 v)
+{
+ sky2_write16(hw, SK_GMAC_REG(port,r), v);
+}
+
+static inline void gma_set_addr(struct sky2_hw *hw, unsigned port, unsigned reg,
+ const u8 *addr)
+{
+ gma_write16(hw, port, reg, (u16) addr[0] | ((u16) addr[1] << 8));
+ gma_write16(hw, port, reg+4,(u16) addr[2] | ((u16) addr[3] << 8));
+ gma_write16(hw, port, reg+8,(u16) addr[4] | ((u16) addr[5] << 8));
+}
+#endif
+++ /dev/null
-#ifndef _LMC_PROTO_H_
-#define _LMC_PROTO_H_
-
-void lmc_proto_init(lmc_softc_t * const)
-void lmc_proto_attach(lmc_softc_t *sc const)
-void lmc_proto_detach(lmc_softc *sc const)
-void lmc_proto_reopen(lmc_softc_t *sc const)
-int lmc_proto_ioctl(lmc_softc_t *sc const, struct ifreq *ifr, int cmd)
-void lmc_proto_open(lmc_softc_t *sc const)
-void lmc_proto_close(lmc_softc_t *sc const)
-unsigned short lmc_proto_type(lmc_softc_t *sc const, struct skbuff *skb)
-
-
-#endif
-
Copyright 2000-2001 ATMEL Corporation.
Copyright 2003-2004 Simon Kelley.
- This code was developed from version 2.1.1 of the Atmel drivers,
- released by Atmel corp. under the GPL in December 2002. It also
- includes code from the Linux aironet drivers (C) Benjamin Reed,
+ This code was developed from version 2.1.1 of the Atmel drivers,
+ released by Atmel corp. under the GPL in December 2002. It also
+ includes code from the Linux aironet drivers (C) Benjamin Reed,
and the Linux PCMCIA package, (C) David Hinds and the Linux wireless
extensions, (C) Jean Tourrilhes.
along with Atmel wireless lan drivers; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- For all queries about this code, please contact the current author,
+ For all queries about this code, please contact the current author,
Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation.
Credit is due to HP UK and Cambridge Online Systems Ltd for supplying
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards");
-/* The name of the firmware file to be loaded
+/* The name of the firmware file to be loaded
over-rides any automatic selection */
static char *firmware = NULL;
module_param(firmware, charp, 0);
/* table of firmware file names */
-static struct {
+static struct {
AtmelFWType fw_type;
const char *fw_file;
const char *fw_file_ext;
#define MAX_SSID_LENGTH 32
#define MGMT_JIFFIES (256 * HZ / 100)
-#define MAX_BSS_ENTRIES 64
+#define MAX_BSS_ENTRIES 64
/* registers */
-#define GCR 0x00 // (SIR0) General Configuration Register
-#define BSR 0x02 // (SIR1) Bank Switching Select Register
+#define GCR 0x00 // (SIR0) General Configuration Register
+#define BSR 0x02 // (SIR1) Bank Switching Select Register
#define AR 0x04
#define DR 0x08
-#define MR1 0x12 // Mirror Register 1
-#define MR2 0x14 // Mirror Register 2
-#define MR3 0x16 // Mirror Register 3
-#define MR4 0x18 // Mirror Register 4
+#define MR1 0x12 // Mirror Register 1
+#define MR2 0x14 // Mirror Register 2
+#define MR3 0x16 // Mirror Register 3
+#define MR4 0x18 // Mirror Register 4
#define GPR1 0x0c
#define GPR2 0x0e
// Constants for the GCR register.
//
#define GCR_REMAP 0x0400 // Remap internal SRAM to 0
-#define GCR_SWRES 0x0080 // BIU reset (ARM and PAI are NOT reset)
+#define GCR_SWRES 0x0080 // BIU reset (ARM and PAI are NOT reset)
#define GCR_CORES 0x0060 // Core Reset (ARM and PAI are reset)
-#define GCR_ENINT 0x0002 // Enable Interrupts
+#define GCR_ENINT 0x0002 // Enable Interrupts
#define GCR_ACKINT 0x0008 // Acknowledge Interrupts
#define BSS_SRAM 0x0200 // AMBA module selection --> SRAM
u32 Next;
u16 MsduPos;
u16 MsduSize;
-
+
u8 State;
u8 Status;
u8 Rate;
u8 PreambleType;
u16 Duration;
u32 RxTime;
-
};
#define RX_DESC_FLAG_VALID 0x80
#define RX_DESC_DURATION_OFFSET 14
#define RX_DESC_RX_TIME_OFFSET 16
-
struct tx_desc {
u32 NextDescriptor;
u16 TxStartOfFrame;
u16 TxLength;
-
+
u8 TxState;
u8 TxStatus;
u8 RetryCount;
-
+
u8 TxRate;
u8 KeyIndex;
u8 Reserved;
u8 PacketType;
u16 HostTxLength;
-
};
-
#define TX_DESC_NEXT_OFFSET 0
#define TX_DESC_POS_OFFSET 4
#define TX_DESC_SIZE_OFFSET 6
#define TX_DESC_PACKET_TYPE_OFFSET 17
#define TX_DESC_HOST_LENGTH_OFFSET 18
-
-
///////////////////////////////////////////////////////
// Host-MAC interface
///////////////////////////////////////////////////////
#define TX_FIRM_OWN 0x80
#define TX_DONE 0x40
-
#define TX_ERROR 0x01
#define TX_PACKET_TYPE_DATA 0x01
#define ISR_COMMAND_COMPLETE 0x10 // command completed
#define ISR_OUT_OF_RANGE 0x20 // command completed
#define ISR_IBSS_MERGE 0x40 // (4.1.2.30): IBSS merge
-#define ISR_GENERIC_IRQ 0x80
-
+#define ISR_GENERIC_IRQ 0x80
#define Local_Mib_Type 0x01
#define Mac_Address_Mib_Type 0x02
#define LOCAL_MIB_PREAMBLE_TYPE 9
#define MAC_ADDR_MIB_MAC_ADDR_POS 0
-
#define CMD_Set_MIB_Vars 0x01
#define CMD_Get_MIB_Vars 0x02
#define CMD_Scan 0x03
#define CMD_STATUS_HOST_ERROR 0xFF
#define CMD_STATUS_BUSY 0xFE
-
#define CMD_BLOCK_COMMAND_OFFSET 0
#define CMD_BLOCK_STATUS_OFFSET 1
#define CMD_BLOCK_PARAMETERS_OFFSET 4
#define MGMT_FRAME_BODY_OFFSET 24
#define MAX_AUTHENTICATION_RETRIES 3
-#define MAX_ASSOCIATION_RETRIES 3
+#define MAX_ASSOCIATION_RETRIES 3
#define AUTHENTICATION_RESPONSE_TIME_OUT 1000
#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */
#define LOOP_RETRY_LIMIT 500000
-#define ACTIVE_MODE 1
-#define PS_MODE 2
+#define ACTIVE_MODE 1
+#define PS_MODE 2
#define MAX_ENCRYPTION_KEYS 4
#define MAX_ENCRYPTION_KEY_SIZE 40
#define REG_DOMAIN_MKK1 0x41 //Channel 1-14 Japan(MKK1)
#define REG_DOMAIN_ISRAEL 0x50 //Channel 3-9 ISRAEL
-#define BSS_TYPE_AD_HOC 1
+#define BSS_TYPE_AD_HOC 1
#define BSS_TYPE_INFRASTRUCTURE 2
#define SCAN_TYPE_ACTIVE 0
#define DATA_FRAME_WS_HEADER_SIZE 30
-/* promiscuous mode control */
+/* promiscuous mode control */
#define PROM_MODE_OFF 0x0
#define PROM_MODE_UNKNOWN 0x1
#define PROM_MODE_CRC_FAILED 0x2
#define PROM_MODE_CTRL 0x10
#define PROM_MODE_BAD_PROTOCOL 0x20
-
-#define IFACE_INT_STATUS_OFFSET 0
+#define IFACE_INT_STATUS_OFFSET 0
#define IFACE_INT_MASK_OFFSET 1
#define IFACE_LOCKOUT_HOST_OFFSET 2
#define IFACE_LOCKOUT_MAC_OFFSET 3
#define IFACE_MAC_STAT_OFFSET 30
#define IFACE_GENERIC_INT_TYPE_OFFSET 32
-#define CIPHER_SUITE_NONE 0
+#define CIPHER_SUITE_NONE 0
#define CIPHER_SUITE_WEP_64 1
#define CIPHER_SUITE_TKIP 2
#define CIPHER_SUITE_AES 3
//
//
-// FuncCtrl field:
+// FuncCtrl field:
//
#define FUNC_CTRL_TxENABLE 0x10
#define FUNC_CTRL_RxENABLE 0x20
-#define FUNC_CTRL_INIT_COMPLETE 0x01
+#define FUNC_CTRL_INIT_COMPLETE 0x01
/* A stub firmware image which reads the MAC address from NVRAM on the card.
For copyright information and source see the end of this file. */
struct net_device_stats stats; // device stats
spinlock_t irqlock, timerlock; // spinlocks
enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type;
- enum {
- CARD_TYPE_PARALLEL_FLASH,
+ enum {
+ CARD_TYPE_PARALLEL_FLASH,
CARD_TYPE_SPI_FLASH,
- CARD_TYPE_EEPROM
+ CARD_TYPE_EEPROM
} card_type;
int do_rx_crc; /* If we need to CRC incoming packets */
int probe_crc; /* set if we don't yet know */
u16 rx_desc_head;
u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous;
u16 tx_free_mem, tx_buff_head, tx_buff_tail;
-
+
u16 frag_seq, frag_len, frag_no;
- u8 frag_source[6];
-
+ u8 frag_source[6];
+
u8 wep_is_on, default_key, exclude_unencrypted, encryption_level;
u8 group_cipher_suite, pairwise_cipher_suite;
u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
- int wep_key_len[MAX_ENCRYPTION_KEYS];
+ int wep_key_len[MAX_ENCRYPTION_KEYS];
int use_wpa, radio_on_broken; /* firmware dependent stuff. */
u16 host_info_base;
- struct host_info_struct {
+ struct host_info_struct {
/* NB this is matched to the hardware, don't change. */
u8 volatile int_status;
u8 volatile int_mask;
u16 rx_buff_size;
u16 rx_desc_pos;
u16 rx_desc_count;
-
+
u16 build_version;
- u16 command_pos;
-
+ u16 command_pos;
+
u16 major_version;
u16 minor_version;
-
+
u16 func_ctrl;
u16 mac_status;
u16 generic_IRQ_type;
u8 reserved[2];
} host_info;
- enum {
+ enum {
STATION_STATE_SCANNING,
STATION_STATE_JOINNING,
STATION_STATE_AUTHENTICATING,
STATION_STATE_DOWN,
STATION_STATE_MGMT_ERROR
} station_state;
-
+
int operating_mode, power_mode;
time_t last_qual;
int beacons_this_sec;
int long_retry, short_retry;
int preamble;
int default_beacon_period, beacon_period, listen_interval;
- int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum;
+ int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum;
int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt;
enum {
SITE_SURVEY_IDLE,
SITE_SURVEY_IN_PROGRESS,
- SITE_SURVEY_COMPLETED
+ SITE_SURVEY_COMPLETED
} site_survey_state;
time_t last_survey;
int station_was_associated, station_is_associated;
int fast_scan;
-
+
struct bss_info {
int channel;
int SSIDsize;
u8 SSID[MAX_SSID_LENGTH];
} BSSinfo[MAX_BSS_ENTRIES];
int BSS_list_entries, current_BSS;
- int connect_to_any_BSS;
+ int connect_to_any_BSS;
int SSID_size, new_SSID_size;
u8 CurrentBSSID[6], BSSID[6];
u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH];
u64 last_beacon_timestamp;
u8 rx_buf[MAX_WIRELESS_BODY];
-
};
static u8 atmel_basic_rates[4] = {0x82,0x84,0x0b,0x16};
static const struct {
int reg_domain;
int min, max;
- char *name;
+ char *name;
} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" },
{ REG_DOMAIN_DOC, 1, 11, "Canada" },
{ REG_DOMAIN_ETSI, 1, 13, "Europe" },
{ REG_DOMAIN_SPAIN, 10, 11, "Spain" },
- { REG_DOMAIN_FRANCE, 10, 13, "France" },
+ { REG_DOMAIN_FRANCE, 10, 13, "France" },
{ REG_DOMAIN_MKK, 14, 14, "MKK" },
{ REG_DOMAIN_MKK1, 1, 14, "MKK1" },
{ REG_DOMAIN_ISRAEL, 3, 9, "Israel"} };
static void build_wpa_mib(struct atmel_private *priv);
static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void atmel_copy_to_card(struct net_device *dev, u16 dest, unsigned char *src, u16 len);
-static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, u16 src, u16 len);
+static void atmel_copy_to_card(struct net_device *dev, u16 dest,
+ unsigned char *src, u16 len);
+static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
+ u16 src, u16 len);
static void atmel_set_gcr(struct net_device *dev, u16 mask);
static void atmel_clear_gcr(struct net_device *dev, u16 mask);
static int atmel_lock_mac(struct atmel_private *priv);
static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data);
static void atmel_command_irq(struct atmel_private *priv);
static int atmel_validate_channel(struct atmel_private *priv, int channel);
-static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr_4addr *header,
+static void atmel_management_frame(struct atmel_private *priv,
+ struct ieee80211_hdr_4addr *header,
u16 frame_len, u8 rssi);
static void atmel_management_timer(u_long a);
-static void atmel_send_command(struct atmel_private *priv, int command, void *cmd, int cmd_size);
-static int atmel_send_command_wait(struct atmel_private *priv, int command, void *cmd, int cmd_size);
-static void atmel_transmit_management_frame(struct atmel_private *priv, struct ieee80211_hdr_4addr *header,
+static void atmel_send_command(struct atmel_private *priv, int command,
+ void *cmd, int cmd_size);
+static int atmel_send_command_wait(struct atmel_private *priv, int command,
+ void *cmd, int cmd_size);
+static void atmel_transmit_management_frame(struct atmel_private *priv,
+ struct ieee80211_hdr_4addr *header,
u8 *body, int body_len);
static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index);
-static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data);
-static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, u16 data);
-static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, u8 *data, int data_len);
-static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, u8 *data, int data_len);
+static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index,
+ u8 data);
+static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index,
+ u16 data);
+static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index,
+ u8 *data, int data_len);
+static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index,
+ u8 *data, int data_len);
static void atmel_scan(struct atmel_private *priv, int specific_ssid);
static void atmel_join_bss(struct atmel_private *priv, int bss_index);
static void atmel_smooth_qual(struct atmel_private *priv);
return priv->host_info.command_pos + offset;
}
-static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc)
+static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc)
{
return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset;
}
-static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc)
+static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc)
{
return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset;
}
static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos)
{
- atmel_writeAR(priv->dev, pos);
+ atmel_writeAR(priv->dev, pos);
return atmel_read8(priv->dev, DR);
}
static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data)
{
- atmel_writeAR(priv->dev, pos);
+ atmel_writeAR(priv->dev, pos);
atmel_write8(priv->dev, DR, data);
}
static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos)
{
- atmel_writeAR(priv->dev, pos);
+ atmel_writeAR(priv->dev, pos);
return atmel_read16(priv->dev, DR);
}
static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data)
{
- atmel_writeAR(priv->dev, pos);
+ atmel_writeAR(priv->dev, pos);
atmel_write16(priv->dev, DR, data);
}
{
int i;
- for (i = 0;
+ for (i = 0;
atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE &&
i < priv->host_info.tx_desc_count;
i++) {
-
u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head));
u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head));
u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head));
priv->tx_buff_head = 0;
else
priv->tx_buff_head += msdu_size;
-
+
if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1))
- priv->tx_desc_head++ ;
+ priv->tx_desc_head++ ;
else
priv->tx_desc_head = 0;
-
+
if (type == TX_PACKET_TYPE_DATA) {
if (status == TX_STATUS_SUCCESS)
priv->stats.tx_packets++;
- else
+ else
priv->stats.tx_errors++;
netif_wake_queue(priv->dev);
}
{
u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail;
- if (priv->tx_desc_free == 3 || priv->tx_free_mem < len)
+ if (priv->tx_desc_free == 3 || priv->tx_free_mem < len)
return 0;
-
+
if (bottom_free >= len)
return priv->host_info.tx_buff_pos + priv->tx_buff_tail;
-
+
if (priv->tx_free_mem - bottom_free >= len) {
priv->tx_buff_tail = 0;
return priv->host_info.tx_buff_pos;
}
-
+
return 0;
}
-static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, u16 len, u16 buff, u8 type)
+static void tx_update_descriptor(struct atmel_private *priv, int is_bcast,
+ u16 len, u16 buff, u8 type)
{
atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff);
atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len);
int cipher_type, cipher_length;
if (is_bcast) {
cipher_type = priv->group_cipher_suite;
- if (cipher_type == CIPHER_SUITE_WEP_64 ||
- cipher_type == CIPHER_SUITE_WEP_128 )
+ if (cipher_type == CIPHER_SUITE_WEP_64 ||
+ cipher_type == CIPHER_SUITE_WEP_128)
cipher_length = 8;
else if (cipher_type == CIPHER_SUITE_TKIP)
cipher_length = 12;
}
} else {
cipher_type = priv->pairwise_cipher_suite;
- if (cipher_type == CIPHER_SUITE_WEP_64 ||
- cipher_type == CIPHER_SUITE_WEP_128 )
+ if (cipher_type == CIPHER_SUITE_WEP_64 ||
+ cipher_type == CIPHER_SUITE_WEP_128)
cipher_length = 8;
else if (cipher_type == CIPHER_SUITE_TKIP)
cipher_length = 12;
cipher_length = 0;
}
}
-
+
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail),
- cipher_type);
+ cipher_type);
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail),
cipher_length);
}
if (priv->tx_desc_previous != priv->tx_desc_tail)
atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0);
priv->tx_desc_previous = priv->tx_desc_tail;
- if (priv->tx_desc_tail < (priv->host_info.tx_desc_count -1 ))
+ if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1))
priv->tx_desc_tail++;
else
priv->tx_desc_tail = 0;
priv->tx_desc_free--;
priv->tx_free_mem -= len;
-
}
-static int start_tx (struct sk_buff *skb, struct net_device *dev)
+static int start_tx(struct sk_buff *skb, struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
struct ieee80211_hdr_4addr header;
unsigned long flags;
u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
u8 SNAP_RFC1024[6] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
-
- if (priv->card && priv->present_callback &&
+
+ if (priv->card && priv->present_callback &&
!(*priv->present_callback)(priv->card)) {
priv->stats.tx_errors++;
dev_kfree_skb(skb);
return 0;
}
-
+
if (priv->station_state != STATION_STATE_READY) {
priv->stats.tx_errors++;
dev_kfree_skb(skb);
return 0;
}
-
+
/* first ensure the timer func cannot run */
- spin_lock_bh(&priv->timerlock);
+ spin_lock_bh(&priv->timerlock);
/* then stop the hardware ISR */
- spin_lock_irqsave(&priv->irqlock, flags);
+ spin_lock_irqsave(&priv->irqlock, flags);
/* nb doing the above in the opposite order will deadlock */
-
+
/* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the
- 12 first bytes (containing DA/SA) and put them in the appropriate fields of
- the Wireless Header. Thus the packet length is then the initial + 18 (+30-12) */
-
+ 12 first bytes (containing DA/SA) and put them in the appropriate
+ fields of the Wireless Header. Thus the packet length is then the
+ initial + 18 (+30-12) */
+
if (!(buff = find_tx_buff(priv, len + 18))) {
priv->stats.tx_dropped++;
spin_unlock_irqrestore(&priv->irqlock, flags);
netif_stop_queue(dev);
return 1;
}
-
+
frame_ctl = IEEE80211_FTYPE_DATA;
header.duration_id = 0;
header.seq_ctl = 0;
memcpy(&header.addr2, dev->dev_addr, 6);
memcpy(&header.addr3, skb->data, 6);
}
-
+
if (priv->use_wpa)
memcpy(&header.addr4, SNAP_RFC1024, 6);
/* Copy the packet sans its 802.3 header addresses which have been replaced */
atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12);
priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE;
-
+
/* low bit of first byte of destination tells us if broadcast */
tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA);
dev->trans_start = jiffies;
priv->stats.tx_bytes += len;
-
+
spin_unlock_irqrestore(&priv->irqlock, flags);
spin_unlock_bh(&priv->timerlock);
dev_kfree_skb(skb);
-
- return 0;
+
+ return 0;
}
-static void atmel_transmit_management_frame(struct atmel_private *priv,
+static void atmel_transmit_management_frame(struct atmel_private *priv,
struct ieee80211_hdr_4addr *header,
u8 *body, int body_len)
{
u16 buff;
- int len = MGMT_FRAME_BODY_OFFSET + body_len;
-
- if (!(buff = find_tx_buff(priv, len)))
+ int len = MGMT_FRAME_BODY_OFFSET + body_len;
+
+ if (!(buff = find_tx_buff(priv, len)))
return;
atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET);
priv->tx_buff_tail += len;
tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT);
}
-
-static void fast_rx_path(struct atmel_private *priv, struct ieee80211_hdr_4addr *header,
+
+static void fast_rx_path(struct atmel_private *priv,
+ struct ieee80211_hdr_4addr *header,
u16 msdu_size, u16 rx_packet_loc, u32 crc)
{
/* fast path: unfragmented packet copy directly into skbuf */
- u8 mac4[6];
- struct sk_buff *skb;
+ u8 mac4[6];
+ struct sk_buff *skb;
unsigned char *skbp;
-
+
/* get the final, mac 4 header field, this tells us encapsulation */
atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6);
msdu_size -= 6;
-
+
if (priv->do_rx_crc) {
crc = crc32_le(crc, mac4, 6);
msdu_size -= 4;
}
-
+
if (!(skb = dev_alloc_skb(msdu_size + 14))) {
priv->stats.rx_dropped++;
return;
skb_reserve(skb, 2);
skbp = skb_put(skb, msdu_size + 12);
atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size);
-
+
if (priv->do_rx_crc) {
u32 netcrc;
crc = crc32_le(crc, skbp + 12, msdu_size);
return;
}
}
-
+
memcpy(skbp, header->addr1, 6); /* destination address */
- if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
memcpy(&skbp[6], header->addr3, 6);
else
memcpy(&skbp[6], header->addr2, 6); /* source address */
-
- priv->dev->last_rx=jiffies;
+
+ priv->dev->last_rx = jiffies;
skb->dev = priv->dev;
skb->protocol = eth_type_trans(skb, priv->dev);
- skb->ip_summed = CHECKSUM_NONE;
+ skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
priv->stats.rx_bytes += 12 + msdu_size;
priv->stats.rx_packets++;
}
/* Test to see if the packet in card memory at packet_loc has a valid CRC
- It doesn't matter that this is slow: it is only used to proble the first few packets. */
+ It doesn't matter that this is slow: it is only used to proble the first few
+ packets. */
static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size)
{
int i = msdu_size - 4;
return 0;
atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4);
-
+
atmel_writeAR(priv->dev, packet_loc);
while (i--) {
u8 octet = atmel_read8(priv->dev, DR);
return (crc ^ 0xffffffff) == netcrc;
}
-static void frag_rx_path(struct atmel_private *priv, struct ieee80211_hdr_4addr *header,
- u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, u8 frag_no, int more_frags)
+static void frag_rx_path(struct atmel_private *priv,
+ struct ieee80211_hdr_4addr *header,
+ u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no,
+ u8 frag_no, int more_frags)
{
- u8 mac4[6];
+ u8 mac4[6];
u8 source[6];
struct sk_buff *skb;
- if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
+ if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
memcpy(source, header->addr3, 6);
else
- memcpy(source, header->addr2, 6);
-
+ memcpy(source, header->addr2, 6);
+
rx_packet_loc += 24; /* skip header */
-
+
if (priv->do_rx_crc)
msdu_size -= 4;
msdu_size -= 6;
rx_packet_loc += 6;
- if (priv->do_rx_crc)
+ if (priv->do_rx_crc)
crc = crc32_le(crc, mac4, 6);
-
+
priv->frag_seq = seq_no;
priv->frag_no = 1;
priv->frag_len = msdu_size;
- memcpy(priv->frag_source, source, 6);
+ memcpy(priv->frag_source, source, 6);
memcpy(&priv->rx_buf[6], source, 6);
memcpy(priv->rx_buf, header->addr1, 6);
-
+
atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size);
if (priv->do_rx_crc) {
memset(priv->frag_source, 0xff, 6);
}
}
-
+
} else if (priv->frag_no == frag_no &&
priv->frag_seq == seq_no &&
memcmp(priv->frag_source, source, 6) == 0) {
-
- atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len],
+
+ atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len],
rx_packet_loc, msdu_size);
if (priv->do_rx_crc) {
u32 netcrc;
- crc = crc32_le(crc,
- &priv->rx_buf[12 + priv->frag_len],
+ crc = crc32_le(crc,
+ &priv->rx_buf[12 + priv->frag_len],
msdu_size);
atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4);
if ((crc ^ 0xffffffff) != netcrc) {
more_frags = 1; /* don't send broken assembly */
}
}
-
+
priv->frag_len += msdu_size;
priv->frag_no++;
priv->stats.rx_dropped++;
} else {
skb_reserve(skb, 2);
- memcpy(skb_put(skb, priv->frag_len + 12),
+ memcpy(skb_put(skb, priv->frag_len + 12),
priv->rx_buf,
priv->frag_len + 12);
priv->dev->last_rx = jiffies;
skb->dev = priv->dev;
skb->protocol = eth_type_trans(skb, priv->dev);
- skb->ip_summed = CHECKSUM_NONE;
+ skb->ip_summed = CHECKSUM_NONE;
netif_rx(skb);
priv->stats.rx_bytes += priv->frag_len + 12;
priv->stats.rx_packets++;
}
}
-
} else
priv->wstats.discard.fragment++;
}
-
+
static void rx_done_irq(struct atmel_private *priv)
{
int i;
struct ieee80211_hdr_4addr header;
-
- for (i = 0;
+
+ for (i = 0;
atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID &&
i < priv->host_info.rx_desc_count;
i++) {
-
+
u16 msdu_size, rx_packet_loc, frame_ctl, seq_control;
u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head));
u32 crc = 0xffffffff;
-
+
if (status != RX_STATUS_SUCCESS) {
if (status == 0xc1) /* determined by experiment */
priv->wstats.discard.nwid++;
else
- priv->stats.rx_errors++;
+ priv->stats.rx_errors++;
goto next;
}
msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head));
rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head));
-
+
if (msdu_size < 30) {
- priv->stats.rx_errors++;
+ priv->stats.rx_errors++;
goto next;
}
-
+
/* Get header as far as end of seq_ctl */
atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24);
frame_ctl = le16_to_cpu(header.frame_ctl);
seq_control = le16_to_cpu(header.seq_ctl);
- /* probe for CRC use here if needed once five packets have arrived with
- the same crc status, we assume we know what's happening and stop probing */
+ /* probe for CRC use here if needed once five packets have
+ arrived with the same crc status, we assume we know what's
+ happening and stop probing */
if (priv->probe_crc) {
if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) {
priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size);
priv->probe_crc = 0;
}
}
-
+
/* don't CRC header when WEP in use */
if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) {
crc = crc32_le(0xffffffff, (unsigned char *)&header, 24);
}
msdu_size -= 24; /* header */
- if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
-
+ if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) {
int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS;
u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG;
u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4;
-
- if (!more_fragments && packet_fragment_no == 0 ) {
+
+ if (!more_fragments && packet_fragment_no == 0) {
fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc);
} else {
frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc,
packet_sequence_no, packet_fragment_no, more_fragments);
}
}
-
+
if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
/* copy rest of packet into buffer */
atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size);
-
+
/* we use the same buffer for frag reassembly and control packets */
memset(priv->frag_source, 0xff, 6);
-
+
if (priv->do_rx_crc) {
/* last 4 octets is crc */
msdu_size -= 4;
atmel_management_frame(priv, &header, msdu_size,
atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head)));
- }
+ }
- next:
+next:
/* release descriptor */
- atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED);
-
+ atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED);
+
if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1))
- priv->rx_desc_head++;
+ priv->rx_desc_head++;
else
priv->rx_desc_head = 0;
}
-}
+}
static irqreturn_t service_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct atmel_private *priv = netdev_priv(dev);
u8 isr;
int i = -1;
- static u8 irq_order[] = {
+ static u8 irq_order[] = {
ISR_OUT_OF_RANGE,
ISR_RxCOMPLETE,
ISR_TxCOMPLETE,
ISR_IBSS_MERGE,
ISR_GENERIC_IRQ
};
-
- if (priv->card && priv->present_callback &&
+ if (priv->card && priv->present_callback &&
!(*priv->present_callback)(priv->card))
return IRQ_HANDLED;
/* In this state upper-level code assumes it can mess with
the card unhampered by interrupts which may change register state.
Note that even though the card shouldn't generate interrupts
- the inturrupt line may be shared. This allows card setup
+ the inturrupt line may be shared. This allows card setup
to go on without disabling interrupts for a long time. */
if (priv->station_state == STATION_STATE_DOWN)
return IRQ_NONE;
-
+
atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */
while (1) {
printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
return IRQ_HANDLED;
}
-
+
isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
-
+
if (!isr) {
atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */
return i == -1 ? IRQ_NONE : IRQ_HANDLED;
}
-
+
atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */
-
+
for (i = 0; i < sizeof(irq_order)/sizeof(u8); i++)
if (isr & irq_order[i])
break;
-
+
if (!atmel_lock_mac(priv)) {
/* failed to contact card */
printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name);
return IRQ_HANDLED;
}
-
+
isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET));
isr ^= irq_order[i];
atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr);
atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
-
+
switch (irq_order[i]) {
-
- case ISR_OUT_OF_RANGE:
- if (priv->operating_mode == IW_MODE_INFRA &&
+
+ case ISR_OUT_OF_RANGE:
+ if (priv->operating_mode == IW_MODE_INFRA &&
priv->station_state == STATION_STATE_READY) {
priv->station_is_associated = 0;
atmel_scan(priv, 1);
priv->wstats.discard.misc++;
/* fall through */
case ISR_RxCOMPLETE:
- rx_done_irq(priv);
+ rx_done_irq(priv);
break;
-
+
case ISR_TxCOMPLETE:
- tx_done_irq(priv);
+ tx_done_irq(priv);
break;
-
+
case ISR_FATAL_ERROR:
printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name);
atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
break;
-
- case ISR_COMMAND_COMPLETE:
+
+ case ISR_COMMAND_COMPLETE:
atmel_command_irq(priv);
break;
case ISR_IBSS_MERGE:
- atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
+ atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
priv->CurrentBSSID, 6);
/* The WPA stuff cares about the current AP address */
if (priv->use_wpa)
printk(KERN_INFO "%s: Generic_irq received.\n", dev->name);
break;
}
- }
+ }
}
-
-static struct net_device_stats *atmel_get_stats (struct net_device *dev)
+static struct net_device_stats *atmel_get_stats(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
return &priv->stats;
}
-static struct iw_statistics *atmel_get_wireless_stats (struct net_device *dev)
+static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
- /* update the link quality here in case we are seeing no beacons
+ /* update the link quality here in case we are seeing no beacons
at all to drive the process */
atmel_smooth_qual(priv);
-
+
priv->wstats.status = priv->station_state;
if (priv->operating_mode == IW_MODE_INFRA) {
| IW_QUAL_NOISE_INVALID;
priv->wstats.miss.beacon = 0;
}
-
- return (&priv->wstats);
+
+ return &priv->wstats;
}
static int atmel_change_mtu(struct net_device *dev, int new_mtu)
static int atmel_set_mac_address(struct net_device *dev, void *p)
{
struct sockaddr *addr = p;
-
+
memcpy (dev->dev_addr, addr->sa_data, dev->addr_len);
return atmel_open(dev);
}
EXPORT_SYMBOL(atmel_open);
-int atmel_open (struct net_device *dev)
+int atmel_open(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
int i, channel;
/* any scheduled timer is no longer needed and might screw things up.. */
del_timer_sync(&priv->management_timer);
-
+
/* Interrupts will not touch the card once in this state... */
priv->station_state = STATION_STATE_DOWN;
priv->site_survey_state = SITE_SURVEY_IDLE;
priv->station_is_associated = 0;
- if (!reset_atmel_card(dev))
+ if (!reset_atmel_card(dev))
return -EAGAIN;
if (priv->config_reg_domain) {
if (i == sizeof(channel_table)/sizeof(channel_table[0])) {
priv->reg_domain = REG_DOMAIN_MKK1;
printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name);
- }
+ }
}
-
+
if ((channel = atmel_validate_channel(priv, priv->channel)))
priv->channel = channel;
- /* this moves station_state on.... */
- atmel_scan(priv, 1);
+ /* this moves station_state on.... */
+ atmel_scan(priv, 1);
atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */
return 0;
}
-static int atmel_close (struct net_device *dev)
+static int atmel_close(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
-
+
atmel_enter_state(priv, STATION_STATE_DOWN);
-
- if (priv->bus_type == BUS_TYPE_PCCARD)
+
+ if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(dev, GCR, 0x0060);
atmel_write16(dev, GCR, 0x0040);
return 0;
int i;
char *p = buf;
char *s, *r, *c;
-
- p += sprintf(p, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR);
-
+
+ p += sprintf(p, "Driver version:\t\t%d.%d\n",
+ DRIVER_MAJOR, DRIVER_MINOR);
+
if (priv->station_state != STATION_STATE_DOWN) {
- p += sprintf(p, "Firmware version:\t%d.%d build %d\nFirmware location:\t",
+ p += sprintf(p, "Firmware version:\t%d.%d build %d\n"
+ "Firmware location:\t",
priv->host_info.major_version,
priv->host_info.minor_version,
priv->host_info.build_version);
-
- if (priv->card_type != CARD_TYPE_EEPROM)
+
+ if (priv->card_type != CARD_TYPE_EEPROM)
p += sprintf(p, "on card\n");
- else if (priv->firmware)
- p += sprintf(p, "%s loaded by host\n", priv->firmware_id);
+ else if (priv->firmware)
+ p += sprintf(p, "%s loaded by host\n",
+ priv->firmware_id);
else
- p += sprintf(p, "%s loaded by hotplug\n", priv->firmware_id);
-
- switch(priv->card_type) {
+ p += sprintf(p, "%s loaded by hotplug\n",
+ priv->firmware_id);
+
+ switch (priv->card_type) {
case CARD_TYPE_PARALLEL_FLASH: c = "Parallel flash"; break;
case CARD_TYPE_SPI_FLASH: c = "SPI flash\n"; break;
case CARD_TYPE_EEPROM: c = "EEPROM"; break;
default: c = "<unknown>";
}
-
r = "<unknown>";
for (i = 0; i < sizeof(channel_table)/sizeof(channel_table[0]); i++)
if (priv->reg_domain == channel_table[i].reg_domain)
r = channel_table[i].name;
-
+
p += sprintf(p, "MAC memory type:\t%s\n", c);
p += sprintf(p, "Regulatory domain:\t%s\n", r);
- p += sprintf(p, "Host CRC checking:\t%s\n",
+ p += sprintf(p, "Host CRC checking:\t%s\n",
priv->do_rx_crc ? "On" : "Off");
p += sprintf(p, "WPA-capable firmware:\t%s\n",
priv->use_wpa ? "Yes" : "No");
}
-
+
switch(priv->station_state) {
case STATION_STATE_SCANNING: s = "Scanning"; break;
case STATION_STATE_JOINNING: s = "Joining"; break;
case STATION_STATE_DOWN: s = "Down"; break;
default: s = "<unknown>";
}
-
+
p += sprintf(p, "Current state:\t\t%s\n", s);
- return p - buf;
+ return p - buf;
}
static int atmel_read_proc(char *page, char **start, off_t off,
return len;
}
-struct net_device *init_atmel_card( unsigned short irq, unsigned long port, const AtmelFWType fw_type,
- struct device *sys_dev, int (*card_present)(void *), void *card)
+struct net_device *init_atmel_card(unsigned short irq, unsigned long port,
+ const AtmelFWType fw_type,
+ struct device *sys_dev,
+ int (*card_present)(void *), void *card)
{
+ struct proc_dir_entry *ent;
struct net_device *dev;
struct atmel_private *priv;
int rc;
/* Create the network device object. */
dev = alloc_etherdev(sizeof(*priv));
if (!dev) {
- printk(KERN_ERR "atmel: Couldn't alloc_etherdev\n");
+ printk(KERN_ERR "atmel: Couldn't alloc_etherdev\n");
return NULL;
}
if (dev_alloc_name(dev, dev->name) < 0) {
- printk(KERN_ERR "atmel: Couldn't get name!\n");
+ printk(KERN_ERR "atmel: Couldn't get name!\n");
goto err_out_free;
}
memset(priv->BSSID, 0, 6);
priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */
priv->station_was_associated = 0;
-
+
priv->last_survey = jiffies;
priv->preamble = LONG_PREAMBLE;
priv->operating_mode = IW_MODE_INFRA;
spin_lock_init(&priv->timerlock);
priv->management_timer.function = atmel_management_timer;
priv->management_timer.data = (unsigned long) dev;
-
+
dev->open = atmel_open;
dev->stop = atmel_close;
dev->change_mtu = atmel_change_mtu;
dev->do_ioctl = atmel_ioctl;
dev->irq = irq;
dev->base_addr = port;
-
+
SET_NETDEV_DEV(dev, sys_dev);
-
+
if ((rc = request_irq(dev->irq, service_interrupt, SA_SHIRQ, dev->name, dev))) {
- printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc );
+ printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc);
goto err_out_free;
}
- if (!request_region(dev->base_addr, 32,
+ if (!request_region(dev->base_addr, 32,
priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) {
goto err_out_irq;
}
-
+
if (register_netdev(dev))
goto err_out_res;
-
+
if (!probe_atmel_card(dev)){
unregister_netdev(dev);
goto err_out_res;
}
-
+
netif_carrier_off(dev);
-
- create_proc_read_entry ("driver/atmel", 0, NULL, atmel_read_proc, priv);
-
+
+ ent = create_proc_read_entry ("driver/atmel", 0, NULL, atmel_read_proc, priv);
+ if (!ent)
+ printk(KERN_WARNING "atmel: unable to create /proc entry.\n");
+
printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
dev->name, DRIVER_MAJOR, DRIVER_MINOR,
dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5] );
-
+
SET_MODULE_OWNER(dev);
return dev;
-
- err_out_res:
+
+err_out_res:
release_region( dev->base_addr, 32);
- err_out_irq:
+err_out_irq:
free_irq(dev->irq, dev);
- err_out_free:
+err_out_free:
free_netdev(dev);
return NULL;
}
void stop_atmel_card(struct net_device *dev)
{
struct atmel_private *priv = netdev_priv(dev);
-
+
/* put a brick on it... */
- if (priv->bus_type == BUS_TYPE_PCCARD)
+ if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(dev, GCR, 0x0060);
atmel_write16(dev, GCR, 0x0040);
-
+
del_timer_sync(&priv->management_timer);
unregister_netdev(dev);
remove_proc_entry("driver/atmel", NULL);
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
priv->connect_to_any_BSS = 0;
-
+
/* Check the size of the string */
if (dwrq->length > MAX_SSID_LENGTH + 1)
- return -E2BIG ;
+ return -E2BIG;
if (index != 0)
return -EINVAL;
-
+
memcpy(priv->new_SSID, extra, dwrq->length - 1);
priv->new_SSID_size = dwrq->length - 1;
}
extra[priv->SSID_size] = '\0';
dwrq->length = priv->SSID_size + 1;
}
-
+
dwrq->flags = !priv->connect_to_any_BSS; /* active */
return 0;
/* WE specify that if a valid key is set, encryption
* should be enabled (user may turn it off later)
* This is also how "iwconfig ethX key on" works */
- if (index == current_index &&
+ if (index == current_index &&
priv->wep_key_len[index] > 0) {
priv->wep_is_on = 1;
priv->exclude_unencrypted = 1;
} else {
/* Do we want to just set the transmit key index ? */
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
- if ( index>=0 && index < 4 ) {
+ if (index >= 0 && index < 4) {
priv->default_key = index;
} else
/* Don't complain if only change the mode */
- if(!dwrq->flags & IW_ENCODE_MODE) {
+ if (!dwrq->flags & IW_ENCODE_MODE) {
return -EINVAL;
}
}
/* Read the flags */
- if(dwrq->flags & IW_ENCODE_DISABLED) {
+ if (dwrq->flags & IW_ENCODE_DISABLED) {
priv->wep_is_on = 0;
- priv->encryption_level = 0;
+ priv->encryption_level = 0;
priv->pairwise_cipher_suite = CIPHER_SUITE_NONE;
} else {
priv->wep_is_on = 1;
priv->encryption_level = 1;
}
}
- if(dwrq->flags & IW_ENCODE_RESTRICTED)
+ if (dwrq->flags & IW_ENCODE_RESTRICTED)
priv->exclude_unencrypted = 1;
- if(dwrq->flags & IW_ENCODE_OPEN)
+ if(dwrq->flags & IW_ENCODE_OPEN)
priv->exclude_unencrypted = 0;
-
+
return -EINPROGRESS; /* Call commit handler */
}
-
static int atmel_get_encode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
{
struct atmel_private *priv = netdev_priv(dev);
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
-
+
if (!priv->wep_is_on)
dwrq->flags = IW_ENCODE_DISABLED;
else {
memset(extra, 0, 16);
memcpy(extra, priv->wep_keys[index], dwrq->length);
}
-
+
return 0;
}
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
-
+
if (vwrq->fixed == 0) {
priv->tx_rate = 3;
priv->auto_tx_rate = 1;
} else {
priv->auto_tx_rate = 0;
-
+
/* Which type of value ? */
- if((vwrq->value < 4) && (vwrq->value >= 0)) {
+ if ((vwrq->value < 4) && (vwrq->value >= 0)) {
/* Setting by rate index */
- priv->tx_rate = vwrq->value;
+ priv->tx_rate = vwrq->value;
} else {
/* Setting by frequency value */
switch (vwrq->value) {
return -EINVAL;
priv->operating_mode = *uwrq;
- return -EINPROGRESS;
+ return -EINPROGRESS;
}
static int atmel_get_mode(struct net_device *dev,
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
-
+
*uwrq = priv->operating_mode;
return 0;
}
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
-
- if(!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) {
- if(vwrq->flags & IW_RETRY_MAX)
+
+ if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) {
+ if (vwrq->flags & IW_RETRY_MAX)
priv->long_retry = vwrq->value;
else if (vwrq->flags & IW_RETRY_MIN)
priv->short_retry = vwrq->value;
priv->long_retry = vwrq->value;
priv->short_retry = vwrq->value;
}
- return -EINPROGRESS;
+ return -EINPROGRESS;
}
-
+
return -EINVAL;
}
vwrq->disabled = 0; /* Can't be disabled */
/* Note : by default, display the min retry number */
- if((vwrq->flags & IW_RETRY_MAX)) {
+ if (vwrq->flags & IW_RETRY_MAX) {
vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
vwrq->value = priv->long_retry;
} else {
vwrq->flags = IW_RETRY_LIMIT;
vwrq->value = priv->short_retry;
- if(priv->long_retry != priv->short_retry)
+ if (priv->long_retry != priv->short_retry)
vwrq->flags |= IW_RETRY_MIN;
}
struct atmel_private *priv = netdev_priv(dev);
int rthr = vwrq->value;
- if(vwrq->disabled)
+ if (vwrq->disabled)
rthr = 2347;
- if((rthr < 0) || (rthr > 2347)) {
+ if ((rthr < 0) || (rthr > 2347)) {
return -EINVAL;
}
priv->rts_threshold = rthr;
-
+
return -EINPROGRESS; /* Call commit handler */
}
char *extra)
{
struct atmel_private *priv = netdev_priv(dev);
-
+
vwrq->value = priv->rts_threshold;
vwrq->disabled = (vwrq->value >= 2347);
vwrq->fixed = 1;
struct atmel_private *priv = netdev_priv(dev);
int fthr = vwrq->value;
- if(vwrq->disabled)
+ if (vwrq->disabled)
fthr = 2346;
- if((fthr < 256) || (fthr > 2346)) {
+ if ((fthr < 256) || (fthr > 2346)) {
return -EINVAL;
}
fthr &= ~0x1; /* Get an even value - is it really needed ??? */
priv->frag_threshold = fthr;
-
+
return -EINPROGRESS; /* Call commit handler */
}
{
struct atmel_private *priv = netdev_priv(dev);
int rc = -EINPROGRESS; /* Call commit handler */
-
+
/* If setting by frequency, convert to a channel */
- if((fwrq->e == 1) &&
- (fwrq->m >= (int) 241200000) &&
- (fwrq->m <= (int) 248700000)) {
+ if ((fwrq->e == 1) &&
+ (fwrq->m >= (int) 241200000) &&
+ (fwrq->m <= (int) 248700000)) {
int f = fwrq->m / 100000;
int c = 0;
- while((c < 14) && (f != frequency_list[c]))
+ while ((c < 14) && (f != frequency_list[c]))
c++;
/* Hack to fall through... */
fwrq->e = 0;
fwrq->m = c + 1;
}
/* Setting by channel number */
- if((fwrq->m > 1000) || (fwrq->e > 0))
+ if ((fwrq->m > 1000) || (fwrq->e > 0))
rc = -EOPNOTSUPP;
else {
int channel = fwrq->m;
priv->channel = channel;
} else {
rc = -EINVAL;
- }
+ }
}
return rc;
}
* This is not an error, while the device perform scanning,
* traffic doesn't flow, so it's a perfect DoS...
* Jean II */
-
+
if (priv->station_state == STATION_STATE_DOWN)
return -EAGAIN;
/* Initiate a scan command */
if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS)
return -EBUSY;
-
+
del_timer_sync(&priv->management_timer);
spin_lock_irqsave(&priv->irqlock, flags);
-
+
priv->site_survey_state = SITE_SURVEY_IN_PROGRESS;
priv->fast_scan = 0;
atmel_scan(priv, 0);
spin_unlock_irqrestore(&priv->irqlock, flags);
-
+
return 0;
}
int i;
char *current_ev = extra;
struct iw_event iwe;
-
+
if (priv->site_survey_state != SITE_SURVEY_COMPLETED)
return -EAGAIN;
-
- for(i=0; i<priv->BSS_list_entries; i++) {
+
+ for (i = 0; i < priv->BSS_list_entries; i++) {
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, 6);
iwe.cmd = SIOCGIWESSID;
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, priv->BSSinfo[i].SSID);
-
+
iwe.cmd = SIOCGIWMODE;
iwe.u.mode = priv->BSSinfo[i].BSStype;
current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_UINT_LEN);
-
+
iwe.cmd = SIOCGIWFREQ;
iwe.u.freq.m = priv->BSSinfo[i].channel;
iwe.u.freq.e = 0;
current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_FREQ_LEN);
-
+
iwe.cmd = SIOCGIWENCODE;
if (priv->BSSinfo[i].UsingWEP)
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0;
current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, NULL);
-
}
/* Length of data */
dwrq->length = (current_ev - extra);
- dwrq->flags = 0;
-
+ dwrq->flags = 0;
+
return 0;
}
{
struct atmel_private *priv = netdev_priv(dev);
struct iw_range *range = (struct iw_range *) extra;
- int k,i,j;
+ int k, i, j;
dwrq->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
break;
}
if (range->num_channels != 0) {
- for(k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) {
+ for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) {
range->freq[k].i = i; /* List index */
- range->freq[k].m = frequency_list[i-1] * 100000;
+ range->freq[k].m = frequency_list[i - 1] * 100000;
range->freq[k++].e = 1; /* Values in table in MHz -> * 10^5 * 10 */
}
range->num_frequency = k;
}
-
+
range->max_qual.qual = 100;
range->max_qual.level = 100;
range->max_qual.noise = 0;
range->encoding_size[1] = 13;
range->num_encoding_sizes = 2;
range->max_encoding_tokens = 4;
-
+
range->pmp_flags = IW_POWER_ON;
range->pmt_flags = IW_POWER_ON;
range->pm_capa = 0;
-
+
range->we_version_source = WIRELESS_EXT;
range->we_version_compiled = WIRELESS_EXT;
range->retry_capa = IW_RETRY_LIMIT ;
if (awrq->sa_family != ARPHRD_ETHER)
return -EINVAL;
-
+
if (memcmp(bcast, awrq->sa_data, 6) == 0) {
del_timer_sync(&priv->management_timer);
spin_lock_irqsave(&priv->irqlock, flags);
spin_unlock_irqrestore(&priv->irqlock, flags);
return 0;
}
-
- for(i=0; i<priv->BSS_list_entries; i++) {
+
+ for (i = 0; i < priv->BSS_list_entries; i++) {
if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) {
if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) {
return -EINVAL;
}
}
}
-
+
return -EINVAL;
}
-
+
static int atmel_config_commit(struct net_device *dev,
struct iw_request_info *info, /* NULL */
void *zwrq, /* NULL */
return atmel_open(dev);
}
-static const iw_handler atmel_handler[] =
+static const iw_handler atmel_handler[] =
{
(iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */
- (iw_handler) atmel_get_name, /* SIOCGIWNAME */
+ (iw_handler) atmel_get_name, /* SIOCGIWNAME */
(iw_handler) NULL, /* SIOCSIWNWID */
(iw_handler) NULL, /* SIOCGIWNWID */
(iw_handler) atmel_set_freq, /* SIOCSIWFREQ */
(iw_handler) atmel_get_freq, /* SIOCGIWFREQ */
(iw_handler) atmel_set_mode, /* SIOCSIWMODE */
(iw_handler) atmel_get_mode, /* SIOCGIWMODE */
- (iw_handler) NULL, /* SIOCSIWSENS */
- (iw_handler) NULL, /* SIOCGIWSENS */
+ (iw_handler) NULL, /* SIOCSIWSENS */
+ (iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL, /* SIOCSIWRANGE */
(iw_handler) atmel_get_range, /* SIOCGIWRANGE */
(iw_handler) NULL, /* SIOCSIWPRIV */
(iw_handler) atmel_set_wap, /* SIOCSIWAP */
(iw_handler) atmel_get_wap, /* SIOCGIWAP */
(iw_handler) NULL, /* -- hole -- */
- (iw_handler) NULL, /* SIOCGIWAPLIST */
+ (iw_handler) NULL, /* SIOCGIWAPLIST */
(iw_handler) atmel_set_scan, /* SIOCSIWSCAN */
(iw_handler) atmel_get_scan, /* SIOCGIWSCAN */
(iw_handler) atmel_set_essid, /* SIOCSIWESSID */
(iw_handler) atmel_get_essid, /* SIOCGIWESSID */
- (iw_handler) NULL, /* SIOCSIWNICKN */
- (iw_handler) NULL, /* SIOCGIWNICKN */
+ (iw_handler) NULL, /* SIOCSIWNICKN */
+ (iw_handler) NULL, /* SIOCGIWNICKN */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) NULL, /* -- hole -- */
(iw_handler) atmel_set_rate, /* SIOCSIWRATE */
(iw_handler) atmel_get_rts, /* SIOCGIWRTS */
(iw_handler) atmel_set_frag, /* SIOCSIWFRAG */
(iw_handler) atmel_get_frag, /* SIOCGIWFRAG */
- (iw_handler) NULL, /* SIOCSIWTXPOW */
- (iw_handler) NULL, /* SIOCGIWTXPOW */
+ (iw_handler) NULL, /* SIOCSIWTXPOW */
+ (iw_handler) NULL, /* SIOCGIWTXPOW */
(iw_handler) atmel_set_retry, /* SIOCSIWRETRY */
(iw_handler) atmel_get_retry, /* SIOCGIWRETRY */
(iw_handler) atmel_set_encode, /* SIOCSIWENCODE */
(iw_handler) atmel_get_power, /* SIOCGIWPOWER */
};
-
-static const iw_handler atmel_private_handler[] =
+static const iw_handler atmel_private_handler[] =
{
NULL, /* SIOCIWFIRSTPRIV */
};
typedef struct atmel_priv_ioctl {
char id[32];
- unsigned char __user *data;
- unsigned short len;
+ unsigned char __user *data;
+ unsigned short len;
} atmel_priv_ioctl;
-
-#define ATMELFWL SIOCIWFIRSTPRIV
-#define ATMELIDIFC ATMELFWL + 1
-#define ATMELRD ATMELFWL + 2
-#define ATMELMAGIC 0x51807
+#define ATMELFWL SIOCIWFIRSTPRIV
+#define ATMELIDIFC ATMELFWL + 1
+#define ATMELRD ATMELFWL + 2
+#define ATMELMAGIC 0x51807
#define REGDOMAINSZ 20
static const struct iw_priv_args atmel_private_args[] = {
-/*{ cmd, set_args, get_args, name } */
- { ATMELFWL, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | sizeof (atmel_priv_ioctl), IW_PRIV_TYPE_NONE, "atmelfwl" },
- { ATMELIDIFC, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "atmelidifc" },
- { ATMELRD, IW_PRIV_TYPE_CHAR | REGDOMAINSZ, IW_PRIV_TYPE_NONE, "regdomain" },
+ {
+ .cmd = ATMELFWL,
+ .set_args = IW_PRIV_TYPE_BYTE
+ | IW_PRIV_SIZE_FIXED
+ | sizeof (atmel_priv_ioctl),
+ .get_args = IW_PRIV_TYPE_NONE,
+ .name = "atmelfwl"
+ }, {
+ .cmd = ATMELIDIFC,
+ .set_args = IW_PRIV_TYPE_NONE,
+ .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "atmelidifc"
+ }, {
+ .cmd = ATMELRD,
+ .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ,
+ .get_args = IW_PRIV_TYPE_NONE,
+ .name = "regdomain"
+ },
};
-static const struct iw_handler_def atmel_handler_def =
+static const struct iw_handler_def atmel_handler_def =
{
.num_standard = sizeof(atmel_handler)/sizeof(iw_handler),
- .num_private = sizeof(atmel_private_handler)/sizeof(iw_handler),
- .num_private_args = sizeof(atmel_private_args)/sizeof(struct iw_priv_args),
+ .num_private = sizeof(atmel_private_handler)/sizeof(iw_handler),
+ .num_private_args = sizeof(atmel_private_args)/sizeof(struct iw_priv_args),
.standard = (iw_handler *) atmel_handler,
- .private = (iw_handler *) atmel_private_handler,
+ .private = (iw_handler *) atmel_private_handler,
.private_args = (struct iw_priv_args *) atmel_private_args,
.get_wireless_stats = atmel_get_wireless_stats
};
atmel_priv_ioctl com;
struct iwreq *wrq = (struct iwreq *) rq;
unsigned char *new_firmware;
- char domain[REGDOMAINSZ+1];
+ char domain[REGDOMAINSZ + 1];
switch (cmd) {
case ATMELIDIFC:
- wrq->u.param.value = ATMELMAGIC;
+ wrq->u.param.value = ATMELMAGIC;
break;
-
+
case ATMELFWL:
if (copy_from_user(&com, rq->ifr_data, sizeof(com))) {
rc = -EFAULT;
}
kfree(priv->firmware);
-
+
priv->firmware = new_firmware;
priv->firmware_length = com.len;
strncpy(priv->firmware_id, com.id, 31);
rc = -EFAULT;
break;
}
-
+
if (!capable(CAP_NET_ADMIN)) {
rc = -EPERM;
break;
rc = 0;
}
}
-
+
if (rc == 0 && priv->station_state != STATION_STATE_DOWN)
rc = atmel_open(dev);
break;
-
+
default:
rc = -EOPNOTSUPP;
}
-
+
return rc;
}
u8 el_id;
u8 chall_text_len;
u8 chall_text[253];
-};
+};
static void atmel_enter_state(struct atmel_private *priv, int new_state)
{
int old_state = priv->station_state;
-
+
if (new_state == old_state)
return;
-
+
priv->station_state = new_state;
-
+
if (new_state == STATION_STATE_READY) {
netif_start_queue(priv->dev);
netif_carrier_on(priv->dev);
u8 options;
u8 SSID_size;
} cmd;
-
+
memset(cmd.BSSID, 0xff, 6);
if (priv->fast_scan) {
cmd.min_channel_time = cpu_to_le16(10);
cmd.max_channel_time = cpu_to_le16(120);
}
-
+
cmd.options = 0;
-
+
if (!specific_ssid)
cmd.options |= SCAN_OPTIONS_SITE_SURVEY;
-
- cmd.channel = (priv->channel & 0x7f);
+
+ cmd.channel = (priv->channel & 0x7f);
cmd.scan_type = SCAN_TYPE_ACTIVE;
- cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ?
+ cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ?
BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE);
-
+
atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd));
/* This must come after all hardware access to avoid being messed up
cmd.BSS_type = type;
cmd.timeout = cpu_to_le16(2000);
- atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd));
+ atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd));
}
-
static void start(struct atmel_private *priv, int type)
{
struct {
u8 BSSID[6];
u8 SSID[MAX_SSID_LENGTH];
- u8 BSS_type;
+ u8 BSS_type;
u8 channel;
u8 SSID_size;
u8 reserved[3];
cmd.BSS_type = type;
cmd.channel = (priv->channel & 0x7f);
- atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd));
+ atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd));
}
-static void handle_beacon_probe(struct atmel_private *priv, u16 capability, u8 channel)
+static void handle_beacon_probe(struct atmel_private *priv, u16 capability,
+ u8 channel)
{
int rejoin = 0;
- int new = capability & C80211_MGMT_CAPABILITY_ShortPreamble ?
+ int new = capability & C80211_MGMT_CAPABILITY_ShortPreamble ?
SHORT_PREAMBLE : LONG_PREAMBLE;
if (priv->preamble != new) {
rejoin = 1;
atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new);
}
-
+
if (priv->channel != channel) {
priv->channel = channel;
rejoin = 1;
atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel);
}
-
+
if (rejoin) {
priv->station_is_associated = 0;
atmel_enter_state(priv, STATION_STATE_JOINNING);
-
+
if (priv->operating_mode == IW_MODE_INFRA)
join(priv, BSS_TYPE_INFRASTRUCTURE);
- else
+ else
join(priv, BSS_TYPE_AD_HOC);
- }
+ }
}
-
-static void send_authentication_request(struct atmel_private *priv, u16 system, u8 *challenge, int challenge_len)
+static void send_authentication_request(struct atmel_private *priv, u16 system,
+ u8 *challenge, int challenge_len)
{
struct ieee80211_hdr_4addr header;
struct auth_body auth;
-
- header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
- header.duration_id = cpu_to_le16(0x8000);
+
+ header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+ header.duration_id = cpu_to_le16(0x8000);
header.seq_ctl = 0;
memcpy(header.addr1, priv->CurrentBSSID, 6);
memcpy(header.addr2, priv->dev->dev_addr, 6);
memcpy(header.addr3, priv->CurrentBSSID, 6);
-
- if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1)
+
+ if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1)
/* no WEP for authentication frames with TrSeqNo 1 */
header.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-
- auth.alg = cpu_to_le16(system);
+
+ auth.alg = cpu_to_le16(system);
auth.status = 0;
auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum);
- priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1;
+ priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1;
priv->CurrentAuthentTransactionSeqNum += 2;
-
+
if (challenge_len != 0) {
auth.el_id = 16; /* challenge_text */
auth.chall_text_len = challenge_len;
struct ieee80211_hdr_4addr header;
struct ass_req_format {
u16 capability;
- u16 listen_interval;
+ u16 listen_interval;
u8 ap[6]; /* nothing after here directly accessible */
u8 ssid_el_id;
u8 ssid_len;
u8 sup_rates_len;
u8 rates[4];
} body;
-
- header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+
+ header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
(is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ));
header.duration_id = cpu_to_le16(0x8000);
header.seq_ctl = 0;
- memcpy(header.addr1, priv->CurrentBSSID, 6);
+ memcpy(header.addr1, priv->CurrentBSSID, 6);
memcpy(header.addr2, priv->dev->dev_addr, 6);
- memcpy(header.addr3, priv->CurrentBSSID, 6);
+ memcpy(header.addr3, priv->CurrentBSSID, 6);
body.capability = cpu_to_le16(C80211_MGMT_CAPABILITY_ESS);
if (priv->wep_is_on)
body.capability |= cpu_to_le16(C80211_MGMT_CAPABILITY_ShortPreamble);
body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period);
-
+
/* current AP address - only in reassoc frame */
if (is_reassoc) {
- memcpy(body.ap, priv->CurrentBSSID, 6);
+ memcpy(body.ap, priv->CurrentBSSID, 6);
ssid_el_p = (u8 *)&body.ssid_el_id;
bodysize = 18 + priv->SSID_size;
} else {
ssid_el_p = (u8 *)&body.ap[0];
bodysize = 12 + priv->SSID_size;
}
-
- ssid_el_p[0]= C80211_MGMT_ElementID_SSID;
+
+ ssid_el_p[0] = C80211_MGMT_ElementID_SSID;
ssid_el_p[1] = priv->SSID_size;
memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size);
ssid_el_p[2 + priv->SSID_size] = C80211_MGMT_ElementID_SupportedRates;
atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize);
}
-static int is_frame_from_current_bss(struct atmel_private *priv, struct ieee80211_hdr_4addr *header)
+static int is_frame_from_current_bss(struct atmel_private *priv,
+ struct ieee80211_hdr_4addr *header)
{
if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS)
return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0;
int i;
int max_rssi = -128;
int max_index = -1;
-
+
if (priv->BSS_list_entries == 0)
return -1;
-
+
if (priv->connect_to_any_BSS) {
- /* Select a BSS with the max-RSSI but of the same type and of the same WEP mode
- and that it is not marked as 'bad' (i.e. we had previously failed to connect to
- this BSS with the settings that we currently use) */
+ /* Select a BSS with the max-RSSI but of the same type and of
+ the same WEP mode and that it is not marked as 'bad' (i.e.
+ we had previously failed to connect to this BSS with the
+ settings that we currently use) */
priv->current_BSS = 0;
- for(i=0; i<priv->BSS_list_entries; i++) {
+ for (i = 0; i < priv->BSS_list_entries; i++) {
if (priv->operating_mode == priv->BSSinfo[i].BSStype &&
- ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) ||
+ ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) ||
(priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) &&
!(priv->BSSinfo[i].channel & 0x80)) {
max_rssi = priv->BSSinfo[i].RSSI;
priv->current_BSS = max_index = i;
}
-
}
return max_index;
}
-
- for(i=0; i<priv->BSS_list_entries; i++) {
+
+ for (i = 0; i < priv->BSS_list_entries; i++) {
if (priv->SSID_size == priv->BSSinfo[i].SSIDsize &&
memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 &&
priv->operating_mode == priv->BSSinfo[i].BSStype &&
return max_index;
}
-
-static void store_bss_info(struct atmel_private *priv, struct ieee80211_hdr_4addr *header,
- u16 capability, u16 beacon_period, u8 channel, u8 rssi,
- u8 ssid_len, u8 *ssid, int is_beacon)
+static void store_bss_info(struct atmel_private *priv,
+ struct ieee80211_hdr_4addr *header, u16 capability,
+ u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len,
+ u8 *ssid, int is_beacon)
{
u8 *bss = capability & C80211_MGMT_CAPABILITY_ESS ? header->addr2 : header->addr3;
int i, index;
-
- for (index = -1, i = 0; i < priv->BSS_list_entries; i++)
- if (memcmp(bss, priv->BSSinfo[i].BSSID, 6) == 0)
+
+ for (index = -1, i = 0; i < priv->BSS_list_entries; i++)
+ if (memcmp(bss, priv->BSSinfo[i].BSSID, 6) == 0)
index = i;
- /* If we process a probe and an entry from this BSS exists
+ /* If we process a probe and an entry from this BSS exists
we will update the BSS entry with the info from this BSS.
If we process a beacon we will only update RSSI */
priv->BSSinfo[index].BSStype = IW_MODE_ADHOC;
else if (capability & C80211_MGMT_CAPABILITY_ESS)
priv->BSSinfo[index].BSStype =IW_MODE_INFRA;
-
- priv->BSSinfo[index].preamble = capability & C80211_MGMT_CAPABILITY_ShortPreamble ?
+
+ priv->BSSinfo[index].preamble = capability & C80211_MGMT_CAPABILITY_ShortPreamble ?
SHORT_PREAMBLE : LONG_PREAMBLE;
}
u16 status = le16_to_cpu(auth->status);
u16 trans_seq_no = le16_to_cpu(auth->trans_seq);
u16 system = le16_to_cpu(auth->alg);
-
- if (status == C80211_MGMT_SC_Success && !priv->wep_is_on) {
+
+ if (status == C80211_MGMT_SC_Success && !priv->wep_is_on) {
/* no WEP */
if (priv->station_was_associated) {
atmel_enter_state(priv, STATION_STATE_REASSOCIATING);
atmel_enter_state(priv, STATION_STATE_ASSOCIATING);
send_association_request(priv, 0);
return;
- }
+ }
}
-
- if (status == C80211_MGMT_SC_Success && priv->wep_is_on) {
+
+ if (status == C80211_MGMT_SC_Success && priv->wep_is_on) {
/* WEP */
if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum)
return;
-
+
if (trans_seq_no == 0x0002 &&
auth->el_id == C80211_MGMT_ElementID_ChallengeText) {
send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len);
return;
}
-
+
if (trans_seq_no == 0x0004) {
if(priv->station_was_associated) {
atmel_enter_state(priv, STATION_STATE_REASSOCIATING);
atmel_enter_state(priv, STATION_STATE_ASSOCIATING);
send_association_request(priv, 0);
return;
- }
+ }
}
- }
-
+ }
+
if (status == C80211_MGMT_SC_AuthAlgNotSupported) {
/* Do opensystem first, then try sharedkey */
if (system == C80211_MGMT_AAN_OPENSYSTEM) {
send_authentication_request(priv, C80211_MGMT_AAN_SHAREDKEY, NULL, 0);
} else if (priv->connect_to_any_BSS) {
int bss_index;
-
+
priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
-
+
if ((bss_index = retrieve_bss(priv)) != -1) {
atmel_join_bss(priv, bss_index);
return;
}
}
}
-
-
+
priv->AuthenticationRequestRetryCnt = 0;
atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
priv->station_is_associated = 0;
u8 length;
u8 rates[4];
} *ass_resp = (struct ass_resp_format *)priv->rx_buf;
-
- u16 status = le16_to_cpu(ass_resp->status);
+
+ u16 status = le16_to_cpu(ass_resp->status);
u16 ass_id = le16_to_cpu(ass_resp->ass_id);
- u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length;
-
+ u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length;
+
if (frame_len < 8 + rates_len)
return;
-
+
if (status == C80211_MGMT_SC_Success) {
if (subtype == C80211_SUBTYPE_MGMT_ASS_RESPONSE)
priv->AssociationRequestRetryCnt = 0;
else
priv->ReAssociationRequestRetryCnt = 0;
-
- atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff);
- atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len);
+
+ atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+ MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff);
+ atmel_set_mib(priv, Phy_Mib_Type,
+ PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len);
if (priv->power_mode == 0) {
priv->listen_interval = 1;
- atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
- atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
+ atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
+ MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
+ atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+ MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
} else {
priv->listen_interval = 2;
- atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, PS_MODE);
- atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2);
+ atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
+ MAC_MGMT_MIB_PS_MODE_POS, PS_MODE);
+ atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+ MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2);
}
-
+
priv->station_is_associated = 1;
priv->station_was_associated = 1;
atmel_enter_state(priv, STATION_STATE_READY);
return;
}
-
+
if (subtype == C80211_SUBTYPE_MGMT_ASS_RESPONSE &&
status != C80211_MGMT_SC_AssDeniedBSSRate &&
status != C80211_MGMT_SC_SupportCapabilities &&
send_association_request(priv, 0);
return;
}
-
+
if (subtype == C80211_SUBTYPE_MGMT_REASS_RESPONSE &&
status != C80211_MGMT_SC_AssDeniedBSSRate &&
status != C80211_MGMT_SC_SupportCapabilities &&
send_association_request(priv, 1);
return;
}
-
+
atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
priv->station_is_associated = 0;
-
- if(priv->connect_to_any_BSS) {
+
+ if (priv->connect_to_any_BSS) {
int bss_index;
priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
-
- if ((bss_index = retrieve_bss(priv)) != -1)
+
+ if ((bss_index = retrieve_bss(priv)) != -1)
atmel_join_bss(priv, bss_index);
-
}
}
/* The WPA stuff cares about the current AP address */
if (priv->use_wpa)
build_wpa_mib(priv);
-
+
/* When switching to AdHoc turn OFF Power Save if needed */
if (bss->BSStype == IW_MODE_ADHOC &&
priv->power_mode) {
priv->power_mode = 0;
priv->listen_interval = 1;
- atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
- atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
+ atmel_set_mib8(priv, Mac_Mgmt_Mib_Type,
+ MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
+ atmel_set_mib16(priv, Mac_Mgmt_Mib_Type,
+ MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
}
-
+
priv->operating_mode = bss->BSStype;
- priv->channel = bss->channel & 0x7f;
+ priv->channel = bss->channel & 0x7f;
priv->beacon_period = bss->beacon_period;
-
+
if (priv->preamble != bss->preamble) {
priv->preamble = bss->preamble;
- atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, bss->preamble);
+ atmel_set_mib8(priv, Local_Mib_Type,
+ LOCAL_MIB_PREAMBLE_TYPE, bss->preamble);
}
-
+
if (!priv->wep_is_on && bss->UsingWEP) {
atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
priv->station_is_associated = 0;
return;
}
-
+
if (priv->wep_is_on && !bss->UsingWEP) {
atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
priv->station_is_associated = 0;
}
atmel_enter_state(priv, STATION_STATE_JOINNING);
-
+
if (priv->operating_mode == IW_MODE_INFRA)
join(priv, BSS_TYPE_INFRASTRUCTURE);
- else
+ else
join(priv, BSS_TYPE_AD_HOC);
}
-
static void restart_search(struct atmel_private *priv)
{
int bss_index;
-
+
if (!priv->connect_to_any_BSS) {
atmel_scan(priv, 1);
} else {
priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80;
-
- if ((bss_index = retrieve_bss(priv)) != -1)
+
+ if ((bss_index = retrieve_bss(priv)) != -1)
atmel_join_bss(priv, bss_index);
else
atmel_scan(priv, 0);
-
- }
-}
+ }
+}
static void smooth_rssi(struct atmel_private *priv, u8 rssi)
{
}
rssi = rssi * 100 / max_rssi;
- if((rssi + old) % 2)
- priv->wstats.qual.level = ((rssi + old)/2) + 1;
+ if ((rssi + old) % 2)
+ priv->wstats.qual.level = (rssi + old) / 2 + 1;
else
- priv->wstats.qual.level = ((rssi + old)/2);
+ priv->wstats.qual.level = (rssi + old) / 2;
priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID;
}
static void atmel_smooth_qual(struct atmel_private *priv)
{
- unsigned long time_diff = (jiffies - priv->last_qual)/HZ;
+ unsigned long time_diff = (jiffies - priv->last_qual) / HZ;
while (time_diff--) {
priv->last_qual += HZ;
- priv->wstats.qual.qual = priv->wstats.qual.qual/2;
- priv->wstats.qual.qual +=
+ priv->wstats.qual.qual = priv->wstats.qual.qual / 2;
+ priv->wstats.qual.qual +=
priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000;
priv->beacons_this_sec = 0;
}
}
/* deals with incoming managment frames. */
-static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr_4addr *header,
- u16 frame_len, u8 rssi)
+static void atmel_management_frame(struct atmel_private *priv,
+ struct ieee80211_hdr_4addr *header,
+ u16 frame_len, u8 rssi)
{
u16 subtype;
-
- switch (subtype = le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_STYPE) {
- case C80211_SUBTYPE_MGMT_BEACON :
+
+ subtype = le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_STYPE;
+ switch (subtype) {
+ case C80211_SUBTYPE_MGMT_BEACON:
case C80211_SUBTYPE_MGMT_ProbeResponse:
-
+
/* beacon frame has multiple variable-length fields -
never let an engineer loose with a data structure design. */
{
u8 ds_length;
/* ds here */
} *beacon = (struct beacon_format *)priv->rx_buf;
-
+
u8 channel, rates_length, ssid_length;
u64 timestamp = le64_to_cpu(beacon->timestamp);
u16 beacon_interval = le16_to_cpu(beacon->interval);
u8 *beaconp = priv->rx_buf;
ssid_length = beacon->ssid_length;
/* this blows chunks. */
- if (frame_len < 14 || frame_len < ssid_length + 15)
+ if (frame_len < 14 || frame_len < ssid_length + 15)
return;
rates_length = beaconp[beacon->ssid_length + 15];
if (frame_len < ssid_length + rates_length + 18)
if (ssid_length > MAX_SSID_LENGTH)
return;
channel = beaconp[ssid_length + rates_length + 18];
-
+
if (priv->station_state == STATION_STATE_READY) {
smooth_rssi(priv, rssi);
- if (is_frame_from_current_bss(priv, header)) {
+ if (is_frame_from_current_bss(priv, header)) {
priv->beacons_this_sec++;
atmel_smooth_qual(priv);
if (priv->last_beacon_timestamp) {
handle_beacon_probe(priv, capability, channel);
}
}
-
- if (priv->station_state == STATION_STATE_SCANNING )
- store_bss_info(priv, header, capability, beacon_interval, channel,
- rssi, ssid_length, &beacon->rates_el_id,
- subtype == C80211_SUBTYPE_MGMT_BEACON) ;
+
+ if (priv->station_state == STATION_STATE_SCANNING)
+ store_bss_info(priv, header, capability,
+ beacon_interval, channel, rssi,
+ ssid_length,
+ &beacon->rates_el_id,
+ subtype == C80211_SUBTYPE_MGMT_BEACON);
}
break;
-
+
case C80211_SUBTYPE_MGMT_Authentication:
if (priv->station_state == STATION_STATE_AUTHENTICATING)
authenticate(priv, frame_len);
-
+
break;
-
+
case C80211_SUBTYPE_MGMT_ASS_RESPONSE:
case C80211_SUBTYPE_MGMT_REASS_RESPONSE:
-
- if (priv->station_state == STATION_STATE_ASSOCIATING ||
+
+ if (priv->station_state == STATION_STATE_ASSOCIATING ||
priv->station_state == STATION_STATE_REASSOCIATING)
associate(priv, frame_len, subtype);
-
+
break;
case C80211_SUBTYPE_MGMT_DISASSOSIATION:
- if (priv->station_is_associated &&
- priv->operating_mode == IW_MODE_INFRA &&
+ if (priv->station_is_associated &&
+ priv->operating_mode == IW_MODE_INFRA &&
is_frame_from_current_bss(priv, header)) {
priv->station_was_associated = 0;
priv->station_is_associated = 0;
-
+
atmel_enter_state(priv, STATION_STATE_JOINNING);
join(priv, BSS_TYPE_INFRASTRUCTURE);
}
-
+
break;
case C80211_SUBTYPE_MGMT_Deauthentication:
atmel_enter_state(priv, STATION_STATE_JOINNING);
join(priv, BSS_TYPE_INFRASTRUCTURE);
}
-
+
break;
}
}
/* run when timer expires */
static void atmel_management_timer(u_long a)
{
- struct net_device *dev = (struct net_device *) a;
- struct atmel_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- /* Check if the card has been yanked. */
- if (priv->card && priv->present_callback &&
- !(*priv->present_callback)(priv->card))
- return;
-
- spin_lock_irqsave(&priv->irqlock, flags);
-
- switch (priv->station_state) {
-
- case STATION_STATE_AUTHENTICATING:
- if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) {
- atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
- priv->station_is_associated = 0;
- priv->AuthenticationRequestRetryCnt = 0;
- restart_search(priv);
- } else {
- priv->AuthenticationRequestRetryCnt++;
- priv->CurrentAuthentTransactionSeqNum = 0x0001;
- mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
- send_authentication_request(priv, C80211_MGMT_AAN_OPENSYSTEM, NULL, 0);
- }
-
- break;
+ struct net_device *dev = (struct net_device *) a;
+ struct atmel_private *priv = netdev_priv(dev);
+ unsigned long flags;
- case STATION_STATE_ASSOCIATING:
- if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
- atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
- priv->station_is_associated = 0;
- priv->AssociationRequestRetryCnt = 0;
- restart_search(priv);
- } else {
- priv->AssociationRequestRetryCnt++;
- mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
- send_association_request(priv, 0);
- }
+ /* Check if the card has been yanked. */
+ if (priv->card && priv->present_callback &&
+ !(*priv->present_callback)(priv->card))
+ return;
- break;
-
- case STATION_STATE_REASSOCIATING:
- if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
- atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
- priv->station_is_associated = 0;
- priv->ReAssociationRequestRetryCnt = 0;
- restart_search(priv);
- } else {
- priv->ReAssociationRequestRetryCnt++;
- mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
- send_association_request(priv, 1);
- }
+ spin_lock_irqsave(&priv->irqlock, flags);
+
+ switch (priv->station_state) {
+ case STATION_STATE_AUTHENTICATING:
+ if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) {
+ atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+ priv->station_is_associated = 0;
+ priv->AuthenticationRequestRetryCnt = 0;
+ restart_search(priv);
+ } else {
+ priv->AuthenticationRequestRetryCnt++;
+ priv->CurrentAuthentTransactionSeqNum = 0x0001;
+ mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+ send_authentication_request(priv, C80211_MGMT_AAN_OPENSYSTEM, NULL, 0);
+ }
break;
-
- default:
+
+ case STATION_STATE_ASSOCIATING:
+ if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
+ atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+ priv->station_is_associated = 0;
+ priv->AssociationRequestRetryCnt = 0;
+ restart_search(priv);
+ } else {
+ priv->AssociationRequestRetryCnt++;
+ mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+ send_association_request(priv, 0);
+ }
break;
- }
-
- spin_unlock_irqrestore(&priv->irqlock, flags);
+
+ case STATION_STATE_REASSOCIATING:
+ if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) {
+ atmel_enter_state(priv, STATION_STATE_MGMT_ERROR);
+ priv->station_is_associated = 0;
+ priv->ReAssociationRequestRetryCnt = 0;
+ restart_search(priv);
+ } else {
+ priv->ReAssociationRequestRetryCnt++;
+ mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
+ send_association_request(priv, 1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&priv->irqlock, flags);
}
-
+
static void atmel_command_irq(struct atmel_private *priv)
{
u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET));
u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET));
int fast_scan;
-
- if (status == CMD_STATUS_IDLE ||
+
+ if (status == CMD_STATUS_IDLE ||
status == CMD_STATUS_IN_PROGRESS)
return;
atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS,
(u8 *)priv->CurrentBSSID, 6);
atmel_enter_state(priv, STATION_STATE_READY);
- }
+ }
break;
-
+
case CMD_Scan:
fast_scan = priv->fast_scan;
priv->fast_scan = 0;
-
+
if (status != CMD_STATUS_COMPLETE) {
atmel_scan(priv, 1);
} else {
int bss_index = retrieve_bss(priv);
if (bss_index != -1) {
atmel_join_bss(priv, bss_index);
- } else if (priv->operating_mode == IW_MODE_ADHOC &&
+ } else if (priv->operating_mode == IW_MODE_ADHOC &&
priv->SSID_size != 0) {
start(priv, BSS_TYPE_AD_HOC);
} else {
priv->site_survey_state = SITE_SURVEY_COMPLETED;
}
break;
-
+
case CMD_SiteSurvey:
priv->fast_scan = 0;
-
+
if (status != CMD_STATUS_COMPLETE)
return;
-
+
priv->site_survey_state = SITE_SURVEY_COMPLETED;
if (priv->station_is_associated) {
- atmel_enter_state(priv, STATION_STATE_READY);
+ atmel_enter_state(priv, STATION_STATE_READY);
} else {
atmel_scan(priv, 1);
}
} else {
priv->AuthenticationRequestRetryCnt = 0;
atmel_enter_state(priv, STATION_STATE_AUTHENTICATING);
-
+
mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES);
priv->CurrentAuthentTransactionSeqNum = 0x0001;
send_authentication_request(priv, C80211_MGMT_AAN_SHAREDKEY, NULL, 0);
}
return;
}
-
+
atmel_scan(priv, 1);
-
}
}
if (priv->card_type == CARD_TYPE_SPI_FLASH)
atmel_set_gcr(priv->dev, GCR_REMAP);
-
+
/* wake up on-board processor */
atmel_clear_gcr(priv->dev, 0x0040);
atmel_write16(priv->dev, BSR, BSS_SRAM);
-
+
if (priv->card_type == CARD_TYPE_SPI_FLASH)
mdelay(100);
/* and wait for it */
- for (i = LOOP_RETRY_LIMIT; i; i--) {
+ for (i = LOOP_RETRY_LIMIT; i; i--) {
mr1 = atmel_read16(priv->dev, MR1);
mr3 = atmel_read16(priv->dev, MR3);
-
- if (mr3 & MAC_BOOT_COMPLETE)
+
+ if (mr3 & MAC_BOOT_COMPLETE)
break;
if (mr1 & MAC_BOOT_COMPLETE &&
priv->bus_type == BUS_TYPE_PCCARD)
printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name);
return 0;
}
-
+
if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) {
printk(KERN_ALERT "%s: card missing.\n", priv->dev->name);
return 0;
}
-
- /* now check for completion of MAC initialization through
- the FunCtrl field of the IFACE, poll MR1 to detect completion of
- MAC initialization, check completion status, set interrupt mask,
- enables interrupts and calls Tx and Rx initialization functions */
-
+
+ /* now check for completion of MAC initialization through
+ the FunCtrl field of the IFACE, poll MR1 to detect completion of
+ MAC initialization, check completion status, set interrupt mask,
+ enables interrupts and calls Tx and Rx initialization functions */
+
atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE);
-
- for (i = LOOP_RETRY_LIMIT; i; i--) {
+
+ for (i = LOOP_RETRY_LIMIT; i; i--) {
mr1 = atmel_read16(priv->dev, MR1);
mr3 = atmel_read16(priv->dev, MR3);
-
- if (mr3 & MAC_INIT_COMPLETE)
+
+ if (mr3 & MAC_INIT_COMPLETE)
break;
if (mr1 & MAC_INIT_COMPLETE &&
priv->bus_type == BUS_TYPE_PCCARD)
break;
}
-
+
if (i == 0) {
- printk(KERN_ALERT "%s: MAC failed to initialise.\n", priv->dev->name);
+ printk(KERN_ALERT "%s: MAC failed to initialise.\n",
+ priv->dev->name);
return 0;
}
-
+
/* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */
if ((mr3 & MAC_INIT_COMPLETE) &&
!(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) {
return 0;
}
- atmel_copy_to_host(priv->dev, (unsigned char *)iface,
+ atmel_copy_to_host(priv->dev, (unsigned char *)iface,
priv->host_info_base, sizeof(*iface));
-
+
iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos);
iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size);
iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos);
{
int rc = 0;
struct atmel_private *priv = netdev_priv(dev);
-
+
/* reset pccard */
- if (priv->bus_type == BUS_TYPE_PCCARD)
+ if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(dev, GCR, 0x0060);
-
+
atmel_write16(dev, GCR, 0x0040);
mdelay(500);
-
+
if (atmel_read16(dev, MR2) == 0) {
- /* No stored firmware so load a small stub which just
+ /* No stored firmware so load a small stub which just
tells us the MAC address */
int i;
priv->card_type = CARD_TYPE_EEPROM;
atmel_set_gcr(dev, GCR_REMAP);
atmel_clear_gcr(priv->dev, 0x0040);
atmel_write16(dev, BSR, BSS_SRAM);
- for (i = LOOP_RETRY_LIMIT; i; i--)
+ for (i = LOOP_RETRY_LIMIT; i; i--)
if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE)
break;
if (i == 0) {
atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6);
/* got address, now squash it again until the network
interface is opened */
- if (priv->bus_type == BUS_TYPE_PCCARD)
+ if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(dev, GCR, 0x0060);
atmel_write16(dev, GCR, 0x0040);
rc = 1;
} else if (atmel_read16(dev, MR4) == 0) {
/* Mac address easy in this case. */
priv->card_type = CARD_TYPE_PARALLEL_FLASH;
- atmel_write16(dev, BSR, 1);
+ atmel_write16(dev, BSR, 1);
atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6);
atmel_write16(dev, BSR, 0x200);
rc = 1;
priv->card_type = CARD_TYPE_SPI_FLASH;
if (atmel_wakeup_firmware(priv)) {
atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6);
-
+
/* got address, now squash it again until the network
interface is opened */
- if (priv->bus_type == BUS_TYPE_PCCARD)
+ if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(dev, GCR, 0x0060);
atmel_write16(dev, GCR, 0x0040);
rc = 1;
}
}
-
+
if (rc) {
if (dev->dev_addr[0] == 0xFF) {
u8 default_mac[] = {0x00,0x04, 0x25, 0x00, 0x00, 0x00};
memcpy(dev->dev_addr, default_mac, 6);
}
}
-
+
return rc;
}
-static void build_wep_mib(struct atmel_private *priv)
/* Move the encyption information on the MIB structure.
This routine is for the pre-WPA firmware: later firmware has
a different format MIB and a different routine. */
+static void build_wep_mib(struct atmel_private *priv)
{
struct { /* NB this is matched to the hardware, don't change. */
- u8 wep_is_on;
+ u8 wep_is_on;
u8 default_key; /* 0..3 */
u8 reserved;
u8 exclude_unencrypted;
-
+
u32 WEPICV_error_count;
u32 WEP_excluded_count;
-
+
u8 wep_keys[MAX_ENCRYPTION_KEYS][13];
- u8 encryption_level; /* 0, 1, 2 */
- u8 reserved2[3];
+ u8 encryption_level; /* 0, 1, 2 */
+ u8 reserved2[3];
} mib;
int i;
if (priv->wep_key_len[priv->default_key] > 5)
mib.encryption_level = 2;
else
- mib.encryption_level = 1;
+ mib.encryption_level = 1;
} else {
mib.encryption_level = 0;
}
mib.default_key = priv->default_key;
mib.exclude_unencrypted = priv->exclude_unencrypted;
-
- for(i = 0; i < MAX_ENCRYPTION_KEYS; i++)
+
+ for (i = 0; i < MAX_ENCRYPTION_KEYS; i++)
memcpy(mib.wep_keys[i], priv->wep_keys[i], 13);
-
+
atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib));
}
static void build_wpa_mib(struct atmel_private *priv)
{
- /* This is for the later (WPA enabled) firmware. */
+ /* This is for the later (WPA enabled) firmware. */
struct { /* NB this is matched to the hardware, don't change. */
u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE];
u8 receiver_address[6];
- u8 wep_is_on;
+ u8 wep_is_on;
u8 default_key; /* 0..3 */
u8 group_key;
u8 exclude_unencrypted;
u8 encryption_type;
u8 reserved;
-
+
u32 WEPICV_error_count;
u32 WEP_excluded_count;
-
+
u8 key_RSC[4][8];
} mib;
-
+
int i;
mib.wep_is_on = priv->wep_is_on;
mib.exclude_unencrypted = priv->exclude_unencrypted;
memcpy(mib.receiver_address, priv->CurrentBSSID, 6);
-
+
/* zero all the keys before adding in valid ones. */
memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value));
-
+
if (priv->wep_is_on) {
- /* There's a comment in the Atmel code to the effect that this is only valid
- when still using WEP, it may need to be set to something to use WPA */
+ /* There's a comment in the Atmel code to the effect that this
+ is only valid when still using WEP, it may need to be set to
+ something to use WPA */
memset(mib.key_RSC, 0, sizeof(mib.key_RSC));
-
+
mib.default_key = mib.group_key = 255;
for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) {
if (priv->wep_key_len[i] > 0) {
if (i == priv->default_key) {
mib.default_key = i;
mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7;
- mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite;
+ mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite;
} else {
mib.group_key = i;
priv->group_cipher_suite = priv->pairwise_cipher_suite;
mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1;
- mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite;
+ mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite;
}
}
}
mib.default_key = mib.group_key != 255 ? mib.group_key : 0;
if (mib.group_key == 255)
mib.group_key = mib.default_key;
-
+
}
-
+
atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib));
}
-
-static int reset_atmel_card(struct net_device *dev)
+
+static int reset_atmel_card(struct net_device *dev)
{
/* do everything necessary to wake up the hardware, including
waiting for the lightning strike and throwing the knife switch....
- set all the Mib values which matter in the card to match
+ set all the Mib values which matter in the card to match
their settings in the atmel_private structure. Some of these
can be altered on the fly, but many (WEP, infrastucture or ad-hoc)
can only be changed by tearing down the world and coming back through
here.
- This routine is also responsible for initialising some
- hardware-specific fields in the atmel_private structure,
+ This routine is also responsible for initialising some
+ hardware-specific fields in the atmel_private structure,
including a copy of the firmware's hostinfo stucture
which is the route into the rest of the firmare datastructures. */
struct atmel_private *priv = netdev_priv(dev);
u8 configuration;
-
+
/* data to add to the firmware names, in priority order
this implemenents firmware versioning */
-
+
static char *firmware_modifier[] = {
"-wpa",
"",
NULL
};
-
+
/* reset pccard */
- if (priv->bus_type == BUS_TYPE_PCCARD)
+ if (priv->bus_type == BUS_TYPE_PCCARD)
atmel_write16(priv->dev, GCR, 0x0060);
-
+
/* stop card , disable interrupts */
atmel_write16(priv->dev, GCR, 0x0040);
-
+
if (priv->card_type == CARD_TYPE_EEPROM) {
/* copy in firmware if needed */
const struct firmware *fw_entry = NULL;
"%s: card type is unknown: assuming at76c502 firmware is OK.\n",
dev->name);
printk(KERN_INFO
- "%s: if not, use the firmware= module parameter.\n",
+ "%s: if not, use the firmware= module parameter.\n",
dev->name);
strcpy(priv->firmware_id, "atmel_at76c502.bin");
}
if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) != 0) {
- printk(KERN_ALERT
- "%s: firmware %s is missing, cannot continue.\n",
+ printk(KERN_ALERT
+ "%s: firmware %s is missing, cannot continue.\n",
dev->name, priv->firmware_id);
return 0;
}
while (fw_table[fw_index].fw_type != priv->firmware_type
&& fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE)
fw_index++;
-
+
/* construct the actual firmware file name */
if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) {
int i;
}
}
if (!success) {
- printk(KERN_ALERT
- "%s: firmware %s is missing, cannot start.\n",
+ printk(KERN_ALERT
+ "%s: firmware %s is missing, cannot start.\n",
dev->name, priv->firmware_id);
priv->firmware_id[0] = '\0';
- return 0;
+ return 0;
}
}
-
+
fw = fw_entry->data;
len = fw_entry->size;
}
-
+
if (len <= 0x6000) {
atmel_write16(priv->dev, BSR, BSS_IRAM);
atmel_copy_to_card(priv->dev, 0, fw, len);
atmel_set_gcr(priv->dev, GCR_REMAP);
} else {
- /* Remap */
+ /* Remap */
atmel_set_gcr(priv->dev, GCR_REMAP);
atmel_write16(priv->dev, BSR, BSS_IRAM);
atmel_copy_to_card(priv->dev, 0, fw, 0x6000);
the 3com broken-ness filter. */
priv->use_wpa = (priv->host_info.major_version == 4);
priv->radio_on_broken = (priv->host_info.major_version == 5);
-
+
/* unmask all irq sources */
atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff);
-
+
/* int Tx system and enable Tx */
atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0);
atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L);
atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0);
atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0);
- priv->tx_desc_free = priv->host_info.tx_desc_count;
- priv->tx_desc_head = 0;
- priv->tx_desc_tail = 0;
+ priv->tx_desc_free = priv->host_info.tx_desc_count;
+ priv->tx_desc_head = 0;
+ priv->tx_desc_tail = 0;
priv->tx_desc_previous = 0;
priv->tx_free_mem = priv->host_info.tx_buff_size;
- priv->tx_buff_head = 0;
- priv->tx_buff_tail = 0;
-
- configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
- atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
+ priv->tx_buff_head = 0;
+ priv->tx_buff_tail = 0;
+
+ configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
+ atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
configuration | FUNC_CTRL_TxENABLE);
/* init Rx system and enable */
priv->rx_desc_head = 0;
-
- configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
- atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
+
+ configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET));
+ atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET),
configuration | FUNC_CTRL_RxENABLE);
-
+
if (!priv->radio_on_broken) {
- if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) ==
+ if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) ==
CMD_STATUS_REJECTED_RADIO_OFF) {
- printk(KERN_INFO
+ printk(KERN_INFO
"%s: cannot turn the radio on. (Hey radio, you're beautiful!)\n",
dev->name);
return 0;
}
}
-
+
/* set up enough MIB values to run. */
atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate);
atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF);
atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry);
atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry);
atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble);
- atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS,
+ atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS,
priv->dev->dev_addr, 6);
atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE);
atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1);
build_wpa_mib(priv);
else
build_wep_mib(priv);
-
+
return 1;
}
-static void atmel_send_command(struct atmel_private *priv, int command, void *cmd, int cmd_size)
+static void atmel_send_command(struct atmel_private *priv, int command,
+ void *cmd, int cmd_size)
{
if (cmd)
- atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET),
+ atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET),
cmd, cmd_size);
-
+
atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command);
atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0);
}
-
-static int atmel_send_command_wait(struct atmel_private *priv, int command, void *cmd, int cmd_size)
+
+static int atmel_send_command_wait(struct atmel_private *priv, int command,
+ void *cmd, int cmd_size)
{
int i, status;
-
+
atmel_send_command(priv, command, cmd, cmd_size);
-
+
for (i = 5000; i; i--) {
status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET));
- if (status != CMD_STATUS_IDLE &&
+ if (status != CMD_STATUS_IDLE &&
status != CMD_STATUS_IN_PROGRESS)
break;
udelay(20);
}
-
+
if (i == 0) {
printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name);
status = CMD_STATUS_HOST_ERROR;
- } else {
+ } else {
if (command != CMD_EnableRadio)
status = CMD_STATUS_COMPLETE;
}
-
+
return status;
}
atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1);
}
-static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, u16 data)
+static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index,
+ u16 data)
{
struct get_set_mib m;
m.type = type;
atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2);
}
-static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, u8 *data, int data_len)
+static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index,
+ u8 *data, int data_len)
{
struct get_set_mib m;
m.type = type;
if (data_len > MIB_MAX_DATA_BYTES)
printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name);
-
+
memcpy(m.data, data, data_len);
atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len);
}
-static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, u8 *data, int data_len)
+static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index,
+ u8 *data, int data_len)
{
struct get_set_mib m;
m.type = type;
m.size = data_len;
m.index = index;
-
+
if (data_len > MIB_MAX_DATA_BYTES)
printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name);
-
+
atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len);
- atmel_copy_to_host(priv->dev, data,
+ atmel_copy_to_host(priv->dev, data,
atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len);
}
int i;
outw(data, dev->base_addr + AR);
/* Address register appears to need some convincing..... */
- for (i = 0; data != inw(dev->base_addr + AR) && i<10; i++)
+ for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++)
outw(data, dev->base_addr + AR);
}
-static void atmel_copy_to_card(struct net_device *dev, u16 dest, unsigned char *src, u16 len)
+static void atmel_copy_to_card(struct net_device *dev, u16 dest,
+ unsigned char *src, u16 len)
{
int i;
atmel_writeAR(dev, dest);
atmel_write8(dev, DR, *src);
}
-static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, u16 src, u16 len)
+static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
+ u16 src, u16 len)
{
int i;
atmel_writeAR(dev, src);
break;
udelay(20);
}
-
- if (!i) return 0; /* timed out */
-
+
+ if (!i)
+ return 0; /* timed out */
+
atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1);
if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) {
atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0);
- if (!j--) return 0; /* timed out */
+ if (!j--)
+ return 0; /* timed out */
goto retry;
}
-
+
return 1;
}
static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data)
{
- atmel_writeAR(priv->dev, pos);
+ atmel_writeAR(priv->dev, pos);
atmel_write16(priv->dev, DR, data); /* card is little-endian */
atmel_write16(priv->dev, DR, data >> 16);
}
serial output, since SO is normally high. But it
does cause 8 clock cycles and thus 8 bits to be
clocked in to the chip. See Atmel's SPI
- controller (e.g. AT91M55800) timing and 4K
+ controller (e.g. AT91M55800) timing and 4K
SPI EEPROM manuals */
-
+
.set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */
.set NVRAM_IMAGE, 0x02000200
.set NVRAM_LENGTH, 0x0200
.set MR4, 0xC
RESET_VECTOR:
b RESET_HANDLER
-UNDEF_VECTOR:
+UNDEF_VECTOR:
b HALT1
-SWI_VECTOR:
+SWI_VECTOR:
b HALT1
-IABORT_VECTOR:
+IABORT_VECTOR:
b HALT1
-DABORT_VECTOR:
-RESERVED_VECTOR:
+DABORT_VECTOR:
+RESERVED_VECTOR:
b HALT1
-IRQ_VECTOR:
+IRQ_VECTOR:
b HALT1
-FIQ_VECTOR:
+FIQ_VECTOR:
b HALT1
HALT1: b HALT1
RESET_HANDLER:
mov r0, #CPSR_INITIAL
msr CPSR_c, r0 /* This is probably unnecessary */
-
+
/* I'm guessing this is initializing clock generator electronics for SPI */
ldr r0, =SPI_CGEN_BASE
mov r1, #0
str r1, [r0, #28]
mov r1, #1
str r1, [r0, #8]
-
+
ldr r0, =MRBASE
mov r1, #0
strh r1, [r0, #MR1]
ldmia sp!, {lr}
bx lr
.endfunc
-
+
.func Get_MAC_Addr, GET_MAC_ADDR
GET_MAC_ADDR:
stmdb sp!, {lr}
.func Delay9, DELAY9
DELAY9:
adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */
-DELAYLOOP:
+DELAYLOOP:
beq DELAY9_done
subs r0, r0, #1
b DELAYLOOP
-DELAY9_done:
+DELAY9_done:
bx lr
-.endfunc
+.endfunc
.func SP_Init, SP_INIT
SP_INIT:
ldr r0, [r0, #SP_RDR]
bx lr
.endfunc
-.func NVRAM_Init, NVRAM_INIT
+.func NVRAM_Init, NVRAM_INIT
NVRAM_INIT:
ldr r1, =SP_BASE
ldr r0, [r1, #SP_RDR]
mov r0, #NVRAM_CMD_RDSR
str r0, [r1, #SP_TDR]
-SP_loop1:
+SP_loop1:
ldr r0, [r1, #SP_SR]
tst r0, #SP_TDRE
beq SP_loop1
mov r0, #SPI_8CLOCKS
- str r0, [r1, #SP_TDR]
-SP_loop2:
+ str r0, [r1, #SP_TDR]
+SP_loop2:
ldr r0, [r1, #SP_SR]
tst r0, #SP_TDRE
beq SP_loop2
ldr r0, [r1, #SP_RDR]
-SP_loop3:
+SP_loop3:
ldr r0, [r1, #SP_SR]
tst r0, #SP_RDRF
beq SP_loop3
and r0, r0, #255
bx lr
.endfunc
-
+
.func NVRAM_Xfer, NVRAM_XFER
/* r0 = dest address */
/* r1 = not used */
mov r4, r3 /* save r3 (length) */
mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */
and r0, r0, #8
- add r0, r0, #NVRAM_CMD_READ
+ add r0, r0, #NVRAM_CMD_READ
ldr r1, =NVRAM_SCRATCH
strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */
strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */
-_local1:
+_local1:
bl NVRAM_INIT
tst r0, #NVRAM_SR_RDY
bne _local1
cmp r0, #0
bls _local2
ldr r5, =NVRAM_SCRATCH
-_local4:
+_local4:
ldrb r6, [r5, r3]
str r6, [r4, #SP_TDR]
_local3:
mov r3, #SPI_8CLOCKS
str r3, [r4, #SP_TDR]
ldr r0, [r4, #SP_RDR]
-_local5:
+_local5:
ldr r0, [r4, #SP_SR]
tst r0, #SP_RDRF
beq _local5
mov r0, #0
cmp r2, #0 /* r2 is # of bytes to copy in */
bls _local6
-_local7:
+_local7:
ldr r5, [r4, #SP_SR]
tst r5, #SP_TDRE
beq _local7
str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */
-_local8:
+_local8:
ldr r5, [r4, #SP_SR]
tst r5, #SP_RDRF
beq _local8
+hostap-y := hostap_main.o
obj-$(CONFIG_HOSTAP) += hostap.o
obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o
extern int netif_rx_ni(struct sk_buff *skb);
#define HAVE_NETIF_RECEIVE_SKB 1
extern int netif_receive_skb(struct sk_buff *skb);
+extern int dev_valid_name(const char *name);
extern int dev_ioctl(unsigned int cmd, void __user *);
extern int dev_ethtool(struct ifreq *);
extern unsigned dev_get_flags(const struct net_device *);
}
/* Schedule rx intr now? */
+static inline int netif_rx_schedule_test(struct net_device *dev)
+{
+ return !test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state);
+}
+/* Schedule only if device is up */
static inline int netif_rx_schedule_prep(struct net_device *dev)
{
- return netif_running(dev) &&
- !test_and_set_bit(__LINK_STATE_RX_SCHED, &dev->state);
+ return netif_running(dev) && netif_rx_schedule_test(dev);
}
/* Add interface to tail of rx poll list. This assumes that _prep has
* Network device names need to be valid file names to
* to allow sysfs to work
*/
-static int dev_valid_name(const char *name)
+int dev_valid_name(const char *name)
{
return !(*name == '\0'
|| !strcmp(name, ".")
EXPORT_SYMBOL(__dev_get_by_name);
EXPORT_SYMBOL(__dev_remove_pack);
EXPORT_SYMBOL(__skb_linearize);
+EXPORT_SYMBOL(dev_valid_name);
EXPORT_SYMBOL(dev_add_pack);
EXPORT_SYMBOL(dev_alloc_name);
EXPORT_SYMBOL(dev_close);
if (*str != '\0')
{
val = 0;
- while (*str != '\0' && *str != '.')
+ while (*str != '\0' && *str != '.' && *str != '\n')
{
val *= 10;
val += *str - '0';