2007-01-21 21:07:46

by Jay Cliburn

[permalink] [raw]
Subject: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver


From: Jay Cliburn <[email protected]>
From: Chris Snook <[email protected]>

This patch contains auxiliary C files for the Attansic L1 gigabit ethernet
adapter driver.

Signed-off-by: Jay Cliburn <[email protected]>
Signed-off-by: Chris Snook <[email protected]>
---

atl1_ethtool.c | 436 ++++++++++++++++++++++++++++++++++
atl1_hw.c | 728 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
atl1_param.c | 223 +++++++++++++++++
3 files changed, 1387 insertions(+)

diff --git a/drivers/net/atl1/atl1_ethtool.c b/drivers/net/atl1/atl1_ethtool.c
new file mode 100644
index 0000000..4c6e505
--- /dev/null
+++ b/drivers/net/atl1/atl1_ethtool.c
@@ -0,0 +1,436 @@
+/**
+ * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+ * Copyright(c) 2006 Chris Snook <[email protected]>
+ * Copyright(c) 2006 Jay Cliburn <[email protected]>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 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.
+ **/
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/mii.h>
+#include <asm/uaccess.h>
+
+#include "atl1.h"
+
+
+extern char atl1_driver_name[];
+extern char atl1_driver_version[];
+
+struct atl1_stats {
+ char stat_string[ETH_GSTRING_LEN];
+ int sizeof_stat;
+ int stat_offset;
+};
+
+#define ATL1_STAT(m) sizeof(((struct atl1_adapter *)0)->m), \
+ offsetof(struct atl1_adapter, m)
+
+static struct atl1_stats atl1_gstrings_stats[] = {
+ {"rx_packets", ATL1_STAT(soft_stats.rx_packets)},
+ {"tx_packets", ATL1_STAT(soft_stats.tx_packets)},
+ {"rx_bytes", ATL1_STAT(soft_stats.rx_bytes)},
+ {"tx_bytes", ATL1_STAT(soft_stats.tx_bytes)},
+ {"rx_errors", ATL1_STAT(soft_stats.rx_errors)},
+ {"tx_errors", ATL1_STAT(soft_stats.tx_errors)},
+ {"rx_dropped", ATL1_STAT(net_stats.rx_dropped)},
+ {"tx_dropped", ATL1_STAT(net_stats.tx_dropped)},
+ {"multicast", ATL1_STAT(soft_stats.multicast)},
+ {"collisions", ATL1_STAT(soft_stats.collisions)},
+ {"rx_length_errors", ATL1_STAT(soft_stats.rx_length_errors)},
+ {"rx_over_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
+ {"rx_crc_errors", ATL1_STAT(soft_stats.rx_crc_errors)},
+ {"rx_frame_errors", ATL1_STAT(soft_stats.rx_frame_errors)},
+ {"rx_fifo_errors", ATL1_STAT(soft_stats.rx_fifo_errors)},
+ {"rx_missed_errors", ATL1_STAT(soft_stats.rx_missed_errors)},
+ {"tx_aborted_errors", ATL1_STAT(soft_stats.tx_aborted_errors)},
+ {"tx_carrier_errors", ATL1_STAT(soft_stats.tx_carrier_errors)},
+ {"tx_fifo_errors", ATL1_STAT(soft_stats.tx_fifo_errors)},
+ {"tx_window_errors", ATL1_STAT(soft_stats.tx_window_errors)},
+ {"tx_abort_exce_coll", ATL1_STAT(soft_stats.excecol)},
+ {"tx_abort_late_coll", ATL1_STAT(soft_stats.latecol)},
+ {"tx_deferred_ok", ATL1_STAT(soft_stats.deffer)},
+ {"tx_single_coll_ok", ATL1_STAT(soft_stats.scc)},
+ {"tx_multi_coll_ok", ATL1_STAT(soft_stats.mcc)},
+ {"tx_underun", ATL1_STAT(soft_stats.tx_underun)},
+ {"tx_trunc", ATL1_STAT(soft_stats.tx_trunc)},
+ {"tx_pause", ATL1_STAT(soft_stats.tx_pause)},
+ {"rx_pause", ATL1_STAT(soft_stats.rx_pause)},
+ {"rx_rrd_ov", ATL1_STAT(soft_stats.rx_rrd_ov)},
+ {"rx_trunc", ATL1_STAT(soft_stats.rx_trunc)}
+};
+
+static void atl1_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ int i;
+ char *p;
+
+ for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
+ p = (char *)adapter+atl1_gstrings_stats[i].stat_offset;
+ data[i] = (atl1_gstrings_stats[i].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ }
+
+}
+
+static int atl1_get_stats_count(struct net_device *netdev)
+{
+ return ARRAY_SIZE(atl1_gstrings_stats);
+}
+
+static int atl1_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ ecmd->supported = (SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_1000baseT_Full |
+ SUPPORTED_Autoneg | SUPPORTED_TP);
+ ecmd->advertising = ADVERTISED_TP;
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) {
+ ecmd->advertising |= ADVERTISED_Autoneg;
+ ecmd->advertising |=
+ (ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Full);
+ } else {
+ ecmd->advertising |= (ADVERTISED_1000baseT_Full);
+ }
+ }
+ ecmd->port = PORT_TP;
+ ecmd->phy_address = 0;
+ ecmd->transceiver = XCVR_INTERNAL;
+
+ if (netif_carrier_ok(adapter->netdev)) {
+ u16 link_speed, link_duplex;
+ atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex);
+ ecmd->speed = link_speed;
+ if (link_duplex == FULL_DUPLEX)
+ ecmd->duplex = DUPLEX_FULL;
+ else
+ ecmd->duplex = DUPLEX_HALF;
+ } else {
+ ecmd->speed = -1;
+ ecmd->duplex = -1;
+ }
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ ecmd->autoneg = AUTONEG_ENABLE;
+ } else {
+ ecmd->autoneg = AUTONEG_DISABLE;
+ }
+
+ return 0;
+}
+
+static int atl1_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+ u16 phy_data;
+ int ret_val = 0;
+ u16 old_media_type = hw->media_type;
+
+ if (netif_running(adapter->netdev)) {
+ printk(KERN_DEBUG "%s: ethtool shutting down link adapter\n",
+ atl1_driver_name);
+ atl1_down(adapter);
+ }
+
+ if (ecmd->autoneg == AUTONEG_ENABLE) {
+ hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
+ } else {
+ if (ecmd->speed == SPEED_1000) {
+ if (ecmd->duplex != DUPLEX_FULL) {
+ printk(KERN_WARNING
+ "%s: can't force to 1000M half duplex\n",
+ atl1_driver_name);
+ ret_val = -EINVAL;
+ goto exit_sset;
+ }
+ hw->media_type = MEDIA_TYPE_1000M_FULL;
+ } else if (ecmd->speed == SPEED_100) {
+ if (ecmd->duplex == DUPLEX_FULL) {
+ hw->media_type = MEDIA_TYPE_100M_FULL;
+ } else {
+ hw->media_type = MEDIA_TYPE_100M_HALF;
+ }
+ } else {
+ if (ecmd->duplex == DUPLEX_FULL) {
+ hw->media_type = MEDIA_TYPE_10M_FULL;
+ } else {
+ hw->media_type = MEDIA_TYPE_10M_HALF;
+ }
+ }
+ }
+ switch (hw->media_type) {
+ case MEDIA_TYPE_AUTO_SENSOR:
+ ecmd->advertising =
+ ADVERTISED_10baseT_Half |
+ ADVERTISED_10baseT_Full |
+ ADVERTISED_100baseT_Half |
+ ADVERTISED_100baseT_Full |
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_TP;
+ break;
+ case MEDIA_TYPE_1000M_FULL:
+ ecmd->advertising =
+ ADVERTISED_1000baseT_Full |
+ ADVERTISED_Autoneg | ADVERTISED_TP;
+ break;
+ default:
+ ecmd->advertising = 0;
+ break;
+ }
+ if (atl1_phy_setup_autoneg_adv(hw)) {
+ ret_val = -EINVAL;
+ printk(KERN_WARNING
+ "%s: invalid ethtool speed/duplex setting\n",
+ atl1_driver_name);
+ goto exit_sset;
+ }
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+ } else {
+ switch (hw->media_type) {
+ case MEDIA_TYPE_100M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+ MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ default: /* MEDIA_TYPE_10M_HALF: */
+ phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ }
+ }
+ atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+exit_sset:
+ if (ret_val) {
+ hw->media_type = old_media_type;
+ }
+ if (netif_running(adapter->netdev)) {
+ printk(KERN_DEBUG "%s: ethtool starting link adapter\n",
+ atl1_driver_name);
+ atl1_up(adapter);
+ } else if (!ret_val) {
+ printk(KERN_DEBUG "%s: ethtool resetting link adapter\n",
+ atl1_driver_name);
+ atl1_reset(adapter);
+ }
+ return ret_val;
+}
+
+static void atl1_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ strncpy(drvinfo->driver, atl1_driver_name, 32);
+ strncpy(drvinfo->version, atl1_driver_version, 32);
+ strncpy(drvinfo->fw_version, "N/A", 32);
+ strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
+ drvinfo->eedump_len = 48;
+}
+
+static void atl1_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
+ wol->wolopts = 0;
+ if (adapter->wol & ATL1_WUFC_EX)
+ wol->wolopts |= WAKE_UCAST;
+ if (adapter->wol & ATL1_WUFC_MC)
+ wol->wolopts |= WAKE_MCAST;
+ if (adapter->wol & ATL1_WUFC_BC)
+ wol->wolopts |= WAKE_BCAST;
+ if (adapter->wol & ATL1_WUFC_MAG)
+ wol->wolopts |= WAKE_MAGIC;
+ return;
+}
+
+static int atl1_set_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+
+ if (wol->wolopts & (WAKE_PHY | WAKE_ARP | WAKE_MAGICSECURE))
+ return -EOPNOTSUPP;
+ adapter->wol = 0;
+ if (wol->wolopts & WAKE_UCAST)
+ adapter->wol |= ATL1_WUFC_EX;
+ if (wol->wolopts & WAKE_MCAST)
+ adapter->wol |= ATL1_WUFC_MC;
+ if (wol->wolopts & WAKE_BCAST)
+ adapter->wol |= ATL1_WUFC_BC;
+ if (wol->wolopts & WAKE_MAGIC)
+ adapter->wol |= ATL1_WUFC_MAG;
+ return 0;
+}
+
+static void atl1_get_ringparam(struct net_device *netdev,
+ struct ethtool_ringparam *ring)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_tpd_ring *txdr = &adapter->tpd_ring;
+ struct atl1_rfd_ring *rxdr = &adapter->rfd_ring;
+
+ ring->rx_max_pending = 2048;
+ ring->tx_max_pending = 1024;
+ ring->rx_mini_max_pending = 0;
+ ring->rx_jumbo_max_pending = 0;
+ ring->rx_pending = rxdr->count;
+ ring->tx_pending = txdr->count;
+ ring->rx_mini_pending = 0;
+ ring->rx_jumbo_pending = 0;
+}
+
+static void atl1_get_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *epause)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ epause->autoneg = AUTONEG_ENABLE;
+ } else {
+ epause->autoneg = AUTONEG_DISABLE;
+ }
+ epause->rx_pause = 1;
+ epause->tx_pause = 1;
+}
+
+static int atl1_set_pauseparam(struct net_device *netdev,
+ struct ethtool_pauseparam *epause)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ epause->autoneg = AUTONEG_ENABLE;
+ } else {
+ epause->autoneg = AUTONEG_DISABLE;
+ }
+
+ epause->rx_pause = 1;
+ epause->tx_pause = 1;
+
+ return 0;
+}
+
+static u32 atl1_get_rx_csum(struct net_device *netdev)
+{
+ return 1;
+}
+
+static void atl1_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
+{
+ u8 *p = data;
+ int i;
+
+ switch (stringset) {
+ case ETH_SS_STATS:
+ for (i = 0; i < ARRAY_SIZE(atl1_gstrings_stats); i++) {
+ memcpy(p, atl1_gstrings_stats[i].stat_string,
+ ETH_GSTRING_LEN);
+ p += ETH_GSTRING_LEN;
+ }
+ break;
+ }
+}
+
+static int atl1_nway_reset(struct net_device *netdev)
+{
+ struct atl1_adapter *adapter = netdev_priv(netdev);
+ struct atl1_hw *hw = &adapter->hw;
+
+ if (netif_running(netdev)) {
+ u16 phy_data;
+ atl1_down(adapter);
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+ } else {
+ switch (hw->media_type) {
+ case MEDIA_TYPE_100M_FULL:
+ phy_data = MII_CR_FULL_DUPLEX |
+ MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ phy_data = MII_CR_FULL_DUPLEX |
+ MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ default: /* MEDIA_TYPE_10M_HALF */
+ phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+ }
+ }
+ atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+ atl1_up(adapter);
+ }
+ return 0;
+}
+
+const struct ethtool_ops atl1_ethtool_ops = {
+ .get_settings = atl1_get_settings,
+ .set_settings = atl1_set_settings,
+ .get_drvinfo = atl1_get_drvinfo,
+ .get_wol = atl1_get_wol,
+ .set_wol = atl1_set_wol,
+ .get_ringparam = atl1_get_ringparam,
+ .get_pauseparam = atl1_get_pauseparam,
+ .set_pauseparam = atl1_set_pauseparam,
+ .get_rx_csum = atl1_get_rx_csum,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = ethtool_op_set_tx_hw_csum,
+ .get_link = ethtool_op_get_link,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = ethtool_op_set_sg,
+ .get_strings = atl1_get_strings,
+ .nway_reset = atl1_nway_reset,
+ .get_ethtool_stats = atl1_get_ethtool_stats,
+ .get_stats_count = atl1_get_stats_count,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = ethtool_op_set_tso,
+};
diff --git a/drivers/net/atl1/atl1_hw.c b/drivers/net/atl1/atl1_hw.c
new file mode 100644
index 0000000..4062abd
--- /dev/null
+++ b/drivers/net/atl1/atl1_hw.c
@@ -0,0 +1,728 @@
+/**
+ * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+ * Copyright(c) 2006 Chris Snook <[email protected]>
+ * Copyright(c) 2006 Jay Cliburn <[email protected]>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 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.
+ **/
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/if_vlan.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <asm/byteorder.h>
+#include "atl1.h"
+
+
+extern char atl1_driver_name[];
+
+/**
+ * Reset the transmit and receive units; mask and clear all interrupts.
+ * hw - Struct containing variables accessed by shared code
+ * return : ATL1_SUCCESS or idle status (if error)
+ **/
+s32 atl1_reset_hw(struct atl1_hw * hw)
+{
+ u32 icr;
+ int i;
+
+ /**
+ * Clear Interrupt mask to stop board from generating
+ * interrupts & Clear any pending interrupt events
+ **/
+ /**
+ * iowrite32(0, hw->hw_addr + REG_IMR);
+ * iowrite32(0xffffffff, hw->hw_addr + REG_ISR);
+ **/
+
+ /**
+ * Issue Soft Reset to the MAC. This will reset the chip's
+ * transmit, receive, DMA. It will not effect
+ * the current PCI configuration. The global reset bit is self-
+ * clearing, and should clear within a microsecond.
+ **/
+ iowrite32(MASTER_CTRL_SOFT_RST, hw->hw_addr + REG_MASTER_CTRL);
+ ioread32(hw->hw_addr + REG_MASTER_CTRL);
+
+ iowrite16(1, hw->hw_addr + REG_GPHY_ENABLE);
+ ioread16(hw->hw_addr + REG_GPHY_ENABLE);
+
+ msleep(1); /* delay about 1ms */
+
+ /* Wait at least 10ms for All module to be Idle */
+ for (i = 0; i < 10; i++) {
+ icr = ioread32(hw->hw_addr + REG_IDLE_STATUS);
+ if (!icr)
+ break;
+ msleep(1); /* delay 1 ms */
+ cpu_relax(); /* FIXME: is this still the right way to do this? */
+ }
+
+ if (icr) {
+ printk (KERN_DEBUG "icr = %x\n", icr);
+ return icr;
+ }
+
+ return ATL1_SUCCESS;
+}
+
+/** function about EEPROM
+ *
+ * check_eeprom_exist
+ * return 0 if eeprom exist
+ **/
+static int atl1_check_eeprom_exist(struct atl1_hw *hw)
+{
+ u32 value;
+ value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+ if (value & SPI_FLASH_CTRL_EN_VPD) {
+ value &= ~SPI_FLASH_CTRL_EN_VPD;
+ iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+ }
+
+ value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST);
+ return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
+}
+
+static bool atl1_read_eeprom(struct atl1_hw *hw, u32 offset, u32 * p_value)
+{
+ int i;
+ u32 control;
+
+ if (offset & 3)
+ return false; /* address do not align */
+
+ iowrite32(0, hw->hw_addr + REG_VPD_DATA);
+ control = (offset & VPD_CAP_VPD_ADDR_MASK) << VPD_CAP_VPD_ADDR_SHIFT;
+ iowrite32(control, hw->hw_addr + REG_VPD_CAP);
+ ioread32(hw->hw_addr + REG_VPD_CAP);
+
+ for (i = 0; i < 10; i++) {
+ msleep(2);
+ control = ioread32(hw->hw_addr + REG_VPD_CAP);
+ if (control & VPD_CAP_VPD_FLAG)
+ break;
+ }
+ if (control & VPD_CAP_VPD_FLAG) {
+ *p_value = ioread32(hw->hw_addr + REG_VPD_DATA);
+ return true;
+ }
+ return false; /* timeout */
+}
+
+/**
+ * Reads the value from a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to read
+ **/
+s32 atl1_read_phy_reg(struct atl1_hw * hw, u16 reg_addr, u16 * phy_data)
+{
+ u32 val;
+ int i;
+
+ val = ((u32) (reg_addr & MDIO_REG_ADDR_MASK)) << MDIO_REG_ADDR_SHIFT |
+ MDIO_START | MDIO_SUP_PREAMBLE | MDIO_RW | MDIO_CLK_25_4 <<
+ MDIO_CLK_SEL_SHIFT;
+ iowrite32(val, hw->hw_addr + REG_MDIO_CTRL);
+ ioread32(hw->hw_addr + REG_MDIO_CTRL);
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY))) {
+ break;
+ }
+ }
+ if (!(val & (MDIO_START | MDIO_BUSY))) {
+ *phy_data = (u16) val;
+ return ATL1_SUCCESS;
+ }
+ return ATL1_ERR_PHY;
+}
+
+#define CUSTOM_SPI_CS_SETUP 2
+#define CUSTOM_SPI_CLK_HI 2
+#define CUSTOM_SPI_CLK_LO 2
+#define CUSTOM_SPI_CS_HOLD 2
+#define CUSTOM_SPI_CS_HI 3
+
+static bool atl1_spi_read(struct atl1_hw *hw, u32 addr, u32 * buf)
+{
+ int i;
+ u32 value;
+
+ iowrite32(0, hw->hw_addr + REG_SPI_DATA);
+ iowrite32(addr, hw->hw_addr + REG_SPI_ADDR);
+
+ value = SPI_FLASH_CTRL_WAIT_READY |
+ (CUSTOM_SPI_CS_SETUP & SPI_FLASH_CTRL_CS_SETUP_MASK) <<
+ SPI_FLASH_CTRL_CS_SETUP_SHIFT | (CUSTOM_SPI_CLK_HI &
+ SPI_FLASH_CTRL_CLK_HI_MASK) <<
+ SPI_FLASH_CTRL_CLK_HI_SHIFT | (CUSTOM_SPI_CLK_LO &
+ SPI_FLASH_CTRL_CLK_LO_MASK) <<
+ SPI_FLASH_CTRL_CLK_LO_SHIFT | (CUSTOM_SPI_CS_HOLD &
+ SPI_FLASH_CTRL_CS_HOLD_MASK) <<
+ SPI_FLASH_CTRL_CS_HOLD_SHIFT | (CUSTOM_SPI_CS_HI &
+ SPI_FLASH_CTRL_CS_HI_MASK) <<
+ SPI_FLASH_CTRL_CS_HI_SHIFT | (1 & SPI_FLASH_CTRL_INS_MASK) <<
+ SPI_FLASH_CTRL_INS_SHIFT;
+
+ iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+
+ value |= SPI_FLASH_CTRL_START;
+ iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
+ ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+
+ for (i = 0; i < 10; i++) {
+ msleep(1); /* 1ms */
+ value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
+ if (!(value & SPI_FLASH_CTRL_START))
+ break;
+ }
+
+ if (value & SPI_FLASH_CTRL_START)
+ return false;
+
+ *buf = ioread32(hw->hw_addr + REG_SPI_DATA);
+
+ return true;
+}
+
+/**
+ * get_permanent_address
+ * return 0 if get valid mac address,
+ **/
+int atl1_get_permanent_address(struct atl1_hw *hw)
+{
+ u32 addr[2];
+ u32 i, control;
+ u16 reg;
+ u8 eth_addr[NODE_ADDRESS_SIZE];
+ bool key_valid;
+
+ if (is_valid_ether_addr(hw->perm_mac_addr))
+ return 0;
+
+ /* init */
+ addr[0] = addr[1] = 0;
+
+ if (!atl1_check_eeprom_exist(hw)) { /* eeprom exist */
+ reg = 0;
+ key_valid = false;
+ /* Read out all EEPROM content */
+ i = 0;
+ while (1) {
+ if (atl1_read_eeprom(hw, i + 0x100, &control)) {
+ if (key_valid) {
+ if (reg == REG_MAC_STA_ADDR)
+ addr[0] = control;
+ else if (reg == (REG_MAC_STA_ADDR + 4)) {
+ addr[1] = control;
+ }
+ key_valid = false;
+ } else if ((control & 0xff) == 0x5A) {
+ key_valid = true;
+ reg = (u16) (control >> 16);
+ } else {
+ break; /* assume data end while encount an invalid KEYWORD */
+ }
+ } else {
+ break; /* read error */
+ }
+ i += 4;
+ }
+
+/**
+ * The following 2 lines are the Attansic originals. Saving for posterity.
+ * *(u32 *) & eth_addr[2] = LONGSWAP(addr[0]);
+ * *(u16 *) & eth_addr[0] = SHORTSWAP(*(u16 *) & addr[1]);
+ **/
+ *(u32 *) & eth_addr[2] = swab32(addr[0]);
+ *(u16 *) & eth_addr[0] = swab16(*(u16 *) & addr[1]);
+
+ if (is_valid_ether_addr(eth_addr)) {
+ memcpy(hw->perm_mac_addr, eth_addr, NODE_ADDRESS_SIZE);
+ return 0;
+ }
+ return 1;
+ }
+
+ /* see if SPI FLAGS exist ? */
+ addr[0] = addr[1] = 0;
+ reg = 0;
+ key_valid = false;
+ i = 0;
+ while (1) {
+ if (atl1_spi_read(hw, i + 0x1f000, &control)) {
+ if (key_valid) {
+ if (reg == REG_MAC_STA_ADDR)
+ addr[0] = control;
+ else if (reg == (REG_MAC_STA_ADDR + 4)) {
+ addr[1] = control;
+ }
+ key_valid = false;
+ } else if ((control & 0xff) == 0x5A) {
+ key_valid = true;
+ reg = (u16) (control >> 16);
+ } else {
+ break; /* data end */
+ }
+ } else {
+ break; /* read error */
+ }
+ i += 4;
+ }
+
+/**
+ * The following 2 lines are the Attansic originals. Saving for posterity.
+ * *(u32 *) & eth_addr[2] = LONGSWAP(addr[0]);
+ * *(u16 *) & eth_addr[0] = SHORTSWAP(*(u16 *) & addr[1]);
+ **/
+ *(u32 *) & eth_addr[2] = swab32(addr[0]);
+ *(u16 *) & eth_addr[0] = swab16(*(u16 *) & addr[1]);
+ if (is_valid_ether_addr(eth_addr)) {
+ memcpy(hw->perm_mac_addr, eth_addr, NODE_ADDRESS_SIZE);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Reads the adapter's MAC address from the EEPROM
+ * hw - Struct containing variables accessed by shared code
+ **/
+s32 atl1_read_mac_addr(struct atl1_hw * hw)
+{
+ u16 i;
+
+ if (atl1_get_permanent_address(hw))
+ random_ether_addr(hw->perm_mac_addr);
+
+ for (i = 0; i < NODE_ADDRESS_SIZE; i++)
+ hw->mac_addr[i] = hw->perm_mac_addr[i];
+ return ATL1_SUCCESS;
+}
+
+/**
+ * Hashes an address to determine its location in the multicast table
+ * hw - Struct containing variables accessed by shared code
+ * mc_addr - the multicast address to hash
+ *
+ * atl1_hash_mc_addr
+ * purpose
+ * set hash value for a multicast address
+ * hash calcu processing :
+ * 1. calcu 32bit CRC for multicast address
+ * 2. reverse crc with MSB to LSB
+ **/
+u32 atl1_hash_mc_addr(struct atl1_hw * hw, u8 * mc_addr)
+{
+ u32 crc32, value = 0;
+ int i;
+
+ crc32 = ether_crc_le(6, mc_addr);
+ crc32 = ~crc32;
+ for (i = 0; i < 32; i++)
+ value |= (((crc32 >> i) & 1) << (31 - i));
+
+ return value;
+}
+
+/**
+ * Sets the bit in the multicast table corresponding to the hash value.
+ * hw - Struct containing variables accessed by shared code
+ * hash_value - Multicast address hash value
+ **/
+void atl1_hash_set(struct atl1_hw *hw, u32 hash_value)
+{
+ u32 hash_bit, hash_reg;
+ u32 mta;
+
+ /**
+ * The HASH Table is a register array of 2 32-bit registers.
+ * It is treated like an array of 64 bits. We want to set
+ * bit BitArray[hash_value]. So we figure out what register
+ * the bit is in, read it, OR in the new bit, then write
+ * back the new value. The register is determined by the
+ * upper 7 bits of the hash value and the bit within that
+ * register are determined by the lower 5 bits of the value.
+ **/
+ hash_reg = (hash_value >> 31) & 0x1;
+ hash_bit = (hash_value >> 26) & 0x1F;
+ mta = ioread32((hw + REG_RX_HASH_TABLE) + (hash_reg << 2));
+ mta |= (1 << hash_bit);
+ iowrite32(mta, (hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2));
+}
+
+/**
+ * Writes a value to a PHY register
+ * hw - Struct containing variables accessed by shared code
+ * reg_addr - address of the PHY register to write
+ * data - data to write to the PHY
+ **/
+s32 atl1_write_phy_reg(struct atl1_hw *hw, u32 reg_addr, u16 phy_data)
+{
+ int i;
+ u32 val;
+
+ val = ((u32) (phy_data & MDIO_DATA_MASK)) << MDIO_DATA_SHIFT |
+ (reg_addr & MDIO_REG_ADDR_MASK) << MDIO_REG_ADDR_SHIFT |
+ MDIO_SUP_PREAMBLE |
+ MDIO_START | MDIO_CLK_25_4 << MDIO_CLK_SEL_SHIFT;
+ iowrite32(val, hw->hw_addr + REG_MDIO_CTRL);
+ ioread32(hw->hw_addr + REG_MDIO_CTRL);
+
+ for (i = 0; i < MDIO_WAIT_TIMES; i++) {
+ udelay(2);
+ val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ return ATL1_SUCCESS;
+
+ return ATL1_ERR_PHY;
+}
+
+/**
+ * Make L001's PHY out of Power Saving State (bug)
+ * hw - Struct containing variables accessed by shared code
+ * when power on, L001's PHY always on Power saving State
+ * (Gigabit Link forbidden)
+ **/
+static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw)
+{
+ s32 ret;
+ ret = atl1_write_phy_reg(hw, 29, 0x0029);
+ if (ret)
+ return ret;
+ return atl1_write_phy_reg(hw, 30, 0);
+}
+
+/**
+ *TODO: do something or get rid of this
+ **/
+s32 atl1_phy_enter_power_saving(struct atl1_hw * hw)
+{
+/* s32 ret_val;
+ * u16 phy_data;
+ */
+
+/*
+ ret_val = atl1_write_phy_reg(hw, ...);
+ ret_val = atl1_write_phy_reg(hw, ...);
+ ....
+*/
+ return ATL1_SUCCESS;
+}
+
+/**
+ * Resets the PHY and make all config validate
+ * hw - Struct containing variables accessed by shared code
+ *
+ * Sets bit 15 and 12 of the MII Control regiser (for F001 bug)
+ **/
+static s32 atl1_phy_reset(struct atl1_hw *hw)
+{
+ s32 ret_val;
+ u16 phy_data;
+
+ if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
+ hw->media_type == MEDIA_TYPE_1000M_FULL) {
+ phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN;
+ } else {
+ switch (hw->media_type) {
+ case MEDIA_TYPE_100M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_100 |
+ MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_100M_HALF:
+ phy_data = MII_CR_SPEED_100 | MII_CR_RESET;
+ break;
+ case MEDIA_TYPE_10M_FULL:
+ phy_data =
+ MII_CR_FULL_DUPLEX | MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ default: /* MEDIA_TYPE_10M_HALF: */
+ phy_data = MII_CR_SPEED_10 | MII_CR_RESET;
+ break;
+ }
+ }
+
+ ret_val = atl1_write_phy_reg(hw, MII_BMCR, phy_data);
+ if (ret_val) {
+ u32 val;
+ int i;
+ /**************************************
+ * pcie serdes link may be down !
+ **************************************/
+ printk(KERN_DEBUG "%s: autoneg caused pcie phy link down\n",
+ atl1_driver_name);
+
+ for (i = 0; i < 25; i++) {
+ msleep(1);
+ val = ioread32(hw->hw_addr + REG_MDIO_CTRL);
+ if (!(val & (MDIO_START | MDIO_BUSY)))
+ break;
+ }
+
+ if ((val & (MDIO_START | MDIO_BUSY)) != 0) {
+ printk(KERN_WARNING
+ "%s: pcie link down at least for 25ms\n",
+ atl1_driver_name);
+ return ret_val;
+ }
+ }
+ return ATL1_SUCCESS;
+}
+
+/**
+ * Configures PHY autoneg and flow control advertisement settings
+ * hw - Struct containing variables accessed by shared code
+ **/
+s32 atl1_phy_setup_autoneg_adv(struct atl1_hw * hw)
+{
+ s32 ret_val;
+ s16 mii_autoneg_adv_reg;
+ s16 mii_1000t_ctrl_reg;
+
+ /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+ mii_autoneg_adv_reg = MII_AR_DEFAULT_CAP_MASK;
+
+ /* Read the MII 1000Base-T Control Register (Address 9). */
+ mii_1000t_ctrl_reg = MII_AT001_CR_1000T_DEFAULT_CAP_MASK;
+
+ /**
+ * First we clear all the 10/100 mb speed bits in the Auto-Neg
+ * Advertisement Register (Address 4) and the 1000 mb speed bits in
+ * the 1000Base-T Control Register (Address 9).
+ **/
+ mii_autoneg_adv_reg &= ~MII_AR_SPEED_MASK;
+ mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK;
+
+ /**
+ * Need to parse media_type and set up
+ * the appropriate PHY registers.
+ **/
+ switch (hw->media_type) {
+ case MEDIA_TYPE_AUTO_SENSOR:
+ mii_autoneg_adv_reg |= (MII_AR_10T_HD_CAPS |
+ MII_AR_10T_FD_CAPS |
+ MII_AR_100TX_HD_CAPS |
+ MII_AR_100TX_FD_CAPS);
+ mii_1000t_ctrl_reg |= MII_AT001_CR_1000T_FD_CAPS;
+ break;
+
+ case MEDIA_TYPE_1000M_FULL:
+ mii_1000t_ctrl_reg |= MII_AT001_CR_1000T_FD_CAPS;
+ break;
+
+ case MEDIA_TYPE_100M_FULL:
+ mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS;
+ break;
+
+ case MEDIA_TYPE_100M_HALF:
+ mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS;
+ break;
+
+ case MEDIA_TYPE_10M_FULL:
+ mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS;
+ break;
+
+ default:
+ mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS;
+ break;
+ }
+
+ /* flow control fixed to enable all */
+ mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE);
+
+ hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg;
+ hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg;
+
+ ret_val = atl1_write_phy_reg(hw, MII_ADVERTISE, mii_autoneg_adv_reg);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = atl1_write_phy_reg(hw, MII_AT001_CR, mii_1000t_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+
+ return ATL1_SUCCESS;
+}
+
+/**
+ * Configures link settings.
+ * hw - Struct containing variables accessed by shared code
+ * Assumes the hardware has previously been reset and the
+ * transmitter and receiver are not enabled.
+ **/
+static s32 atl1_setup_link(struct atl1_hw *hw)
+{
+ s32 ret_val;
+
+ /**
+ * Options:
+ * PHY will advertise value(s) parsed from
+ * autoneg_advertised and fc
+ * no matter what autoneg is , We will not wait link result.
+ **/
+ ret_val = atl1_phy_setup_autoneg_adv(hw);
+ if (ret_val) {
+ printk(KERN_DEBUG "%s: error setting up autonegotiation\n",
+ atl1_driver_name);
+ return ret_val;
+ }
+ /* SW.Reset , En-Auto-Neg if needed */
+ ret_val = atl1_phy_reset(hw);
+ if (ret_val) {
+ printk(KERN_DEBUG "%s: error resetting the phy\n", atl1_driver_name);
+ return ret_val;
+ }
+ hw->phy_configured = true;
+ return ret_val;
+}
+
+struct atl1_spi_flash_dev flash_table[] = {
+/* MFR_NAME WRSR READ PRGM WREN WRDI RDSR RDID SECTOR_ERASE CHIP_ERASE */
+ {"Atmel", 0x00, 0x03, 0x02, 0x06, 0x04, 0x05, 0x15, 0x52, 0x62},
+ {"SST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0x90, 0x20, 0x60},
+ {"ST", 0x01, 0x03, 0x02, 0x06, 0x04, 0x05, 0xAB, 0xD8, 0xC7},
+};
+
+static void atl1_init_flash_opcode(struct atl1_hw *hw)
+{
+ if (hw->flash_vendor >= sizeof(flash_table) / sizeof(flash_table[0]))
+ hw->flash_vendor = 0; /* ATMEL */
+
+ /* Init OP table */
+ iowrite8(flash_table[hw->flash_vendor].cmd_program,
+ hw->hw_addr + REG_SPI_FLASH_OP_PROGRAM);
+ iowrite8(flash_table[hw->flash_vendor].cmd_sector_erase,
+ hw->hw_addr + REG_SPI_FLASH_OP_SC_ERASE);
+ iowrite8(flash_table[hw->flash_vendor].cmd_chip_erase,
+ hw->hw_addr + REG_SPI_FLASH_OP_CHIP_ERASE);
+ iowrite8(flash_table[hw->flash_vendor].cmd_rdid,
+ hw->hw_addr + REG_SPI_FLASH_OP_RDID);
+ iowrite8(flash_table[hw->flash_vendor].cmd_wren,
+ hw->hw_addr + REG_SPI_FLASH_OP_WREN);
+ iowrite8(flash_table[hw->flash_vendor].cmd_rdsr,
+ hw->hw_addr + REG_SPI_FLASH_OP_RDSR);
+ iowrite8(flash_table[hw->flash_vendor].cmd_wrsr,
+ hw->hw_addr + REG_SPI_FLASH_OP_WRSR);
+ iowrite8(flash_table[hw->flash_vendor].cmd_read,
+ hw->hw_addr + REG_SPI_FLASH_OP_READ);
+}
+
+/**
+ * Performs basic configuration of the adapter.
+ * hw - Struct containing variables accessed by shared code
+ * Assumes that the controller has previously been reset and is in a
+ * post-reset uninitialized state. Initializes multicast table,
+ * and Calls routines to setup link
+ * Leaves the transmit and receive units disabled and uninitialized.
+ **/
+s32 atl1_init_hw(struct atl1_hw *hw)
+{
+ u32 ret_val = 0;
+
+ /* Zero out the Multicast HASH table */
+ iowrite32(0, hw->hw_addr + REG_RX_HASH_TABLE);
+ /* clear the old settings from the multicast hash table */
+ iowrite32(0, (hw->hw_addr + REG_RX_HASH_TABLE) + (1 << 2));
+
+ atl1_init_flash_opcode(hw);
+
+ if (!hw->phy_configured) {
+ /* enable GPHY LinkChange Interrrupt */
+ ret_val = atl1_write_phy_reg(hw, 18, 0xC00);
+ if (ret_val)
+ return ret_val;
+ /* make PHY out of power-saving state */
+ ret_val = atl1_phy_leave_power_saving(hw);
+ if (ret_val)
+ return ret_val;
+ /* Call a subroutine to configure the link */
+ ret_val = atl1_setup_link(hw);
+ }
+ return ret_val;
+}
+
+/**
+ * Detects the current speed and duplex settings of the hardware.
+ * hw - Struct containing variables accessed by shared code
+ * speed - Speed of the connection
+ * duplex - Duplex setting of the connection
+ **/
+s32 atl1_get_speed_and_duplex(struct atl1_hw * hw, u16 * speed, u16 * duplex)
+{
+ s32 ret_val;
+ u16 phy_data;
+
+ /* ; --- Read PHY Specific Status Register (17) */
+ ret_val = atl1_read_phy_reg(hw, MII_AT001_PSSR, &phy_data);
+ if (ret_val)
+ return ret_val;
+
+ if (!(phy_data & MII_AT001_PSSR_SPD_DPLX_RESOLVED))
+ return ATL1_ERR_PHY_RES;
+
+ switch (phy_data & MII_AT001_PSSR_SPEED) {
+ case MII_AT001_PSSR_1000MBS:
+ *speed = SPEED_1000;
+ break;
+ case MII_AT001_PSSR_100MBS:
+ *speed = SPEED_100;
+ break;
+ case MII_AT001_PSSR_10MBS:
+ *speed = SPEED_10;
+ break;
+ default:
+ printk(KERN_DEBUG "%s: error getting speed\n",
+ atl1_driver_name);
+ return ATL1_ERR_PHY_SPEED;
+ break;
+ }
+ if (phy_data & MII_AT001_PSSR_DPLX)
+ *duplex = FULL_DUPLEX;
+ else
+ *duplex = HALF_DUPLEX;
+
+ return ATL1_SUCCESS;
+}
+
+void atl1_set_mac_addr(struct atl1_hw *hw)
+{
+ u32 value;
+ /**
+ * 00-0B-6A-F6-00-DC
+ * 0: 6AF600DC 1: 000B
+ * low dword
+ **/
+ value = (((u32) hw->mac_addr[2]) << 24) |
+ (((u32) hw->mac_addr[3]) << 16) |
+ (((u32) hw->mac_addr[4]) << 8) | (((u32) hw->mac_addr[5]));
+ iowrite32(value, hw->hw_addr + REG_MAC_STA_ADDR);
+ /* high dword */
+ value = (((u32) hw->mac_addr[0]) << 8) | (((u32) hw->mac_addr[1]));
+ iowrite32(value, (hw->hw_addr + REG_MAC_STA_ADDR) + (1 << 2));
+}
diff --git a/drivers/net/atl1/atl1_param.c b/drivers/net/atl1/atl1_param.c
new file mode 100644
index 0000000..4732f43
--- /dev/null
+++ b/drivers/net/atl1/atl1_param.c
@@ -0,0 +1,223 @@
+/**
+ * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
+ * Copyright(c) 2006 Chris Snook <[email protected]>
+ * Copyright(c) 2006 Jay Cliburn <[email protected]>
+ *
+ * Derived from Intel e1000 driver
+ * Copyright(c) 1999 - 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.
+ **/
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include "atl1.h"
+
+extern char atl1_driver_name[];
+
+/**
+ * This is the only thing that needs to be changed to adjust the
+ * maximum number of ports that the driver can manage.
+ **/
+#define ATL1_MAX_NIC 4
+
+#define OPTION_UNSET -1
+#define OPTION_DISABLED 0
+#define OPTION_ENABLED 1
+
+#define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET }
+
+/**
+ * Interrupt Moderate Timer in units of 2 us
+ *
+ * Valid Range: 10-65535
+ *
+ * Default Value: 100 (200us)
+ **/
+static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT;
+static int num_int_mod_timer = 0;
+module_param_array_named(int_mod_timer, int_mod_timer, int, &num_int_mod_timer, 0);
+MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer");
+
+/**
+ * flash_vendor
+ *
+ * Valid Range: 0-2
+ *
+ * 0 - Atmel
+ * 1 - SST
+ * 2 - ST
+ *
+ * Default Value: 0
+ **/
+static int __devinitdata flash_vendor[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT;
+static int num_flash_vendor = 0;
+module_param_array_named(flash_vendor, flash_vendor, int, &num_flash_vendor, 0);
+MODULE_PARM_DESC(flash_vendor, "SPI flash vendor");
+
+int enable_msi;
+module_param(enable_msi, int, 0444);
+MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");
+
+#define DEFAULT_INT_MOD_CNT 100 /* 200us */
+#define MAX_INT_MOD_CNT 65000
+#define MIN_INT_MOD_CNT 50
+
+#define FLASH_VENDOR_DEFAULT 0
+#define FLASH_VENDOR_MIN 0
+#define FLASH_VENDOR_MAX 2
+
+struct atl1_option {
+ enum { enable_option, range_option, list_option } type;
+ char *name;
+ char *err;
+ int def;
+ union {
+ struct { /* range_option info */
+ int min;
+ int max;
+ } r;
+ struct { /* list_option info */
+ int nr;
+ struct atl1_opt_list {
+ int i;
+ char *str;
+ } *p;
+ } l;
+ } arg;
+};
+
+static int __devinit atl1_validate_option(int *value, struct atl1_option *opt)
+{
+ if (*value == OPTION_UNSET) {
+ *value = opt->def;
+ return 0;
+ }
+
+ switch (opt->type) {
+ case enable_option:
+ switch (*value) {
+ case OPTION_ENABLED:
+ printk(KERN_INFO "%s: %s Enabled\n", atl1_driver_name,
+ opt->name);
+ return 0;
+ case OPTION_DISABLED:
+ printk(KERN_INFO "%s: %s Disabled\n", atl1_driver_name,
+ opt->name);
+ return 0;
+ }
+ break;
+ case range_option:
+ if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) {
+ printk(KERN_INFO "%s: %s set to %i\n",
+ atl1_driver_name, opt->name, *value);
+ return 0;
+ }
+ break;
+ case list_option:{
+ int i;
+ struct atl1_opt_list *ent;
+
+ for (i = 0; i < opt->arg.l.nr; i++) {
+ ent = &opt->arg.l.p[i];
+ if (*value == ent->i) {
+ if (ent->str[0] != '\0')
+ printk(KERN_INFO "%s: %s\n",
+ atl1_driver_name, ent->str);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ printk(KERN_INFO "%s: invalid %s specified (%i) %s\n",
+ atl1_driver_name, opt->name, *value, opt->err);
+ *value = opt->def;
+ return -1;
+}
+
+/**
+ * atl1_check_options - Range Checking for Command Line Parameters
+ * @adapter: board private structure
+ *
+ * This routine checks all command line parameters for valid user
+ * input. If an invalid value is given, or if no user specified
+ * value exists, a default value is used. The final value is stored
+ * in a variable in the adapter structure.
+ **/
+void __devinit atl1_check_options(struct atl1_adapter *adapter)
+{
+ int bd = adapter->bd_number;
+ if (bd >= ATL1_MAX_NIC) {
+ printk(KERN_NOTICE "%s: warning: no configuration for board #%i\n",
+ atl1_driver_name, bd);
+ printk(KERN_NOTICE "%s: using defaults for all values\n",
+ atl1_driver_name);
+ }
+ { /* Interrupt Moderate Timer */
+ struct atl1_option opt = {
+ .type = range_option,
+ .name = "Interrupt Moderator Timer",
+ .err = "using default of "
+ __MODULE_STRING(DEFAULT_INT_MOD_CNT),
+ .def = DEFAULT_INT_MOD_CNT,
+ .arg = {.r =
+ {.min = MIN_INT_MOD_CNT,.max = MAX_INT_MOD_CNT}}
+ };
+ int val;
+ if (num_int_mod_timer > bd) {
+ val = int_mod_timer[bd];
+ atl1_validate_option(&val, &opt);
+ adapter->imt = (u16) val;
+ } else {
+ adapter->imt = (u16) (opt.def);
+ }
+ }
+
+ { /* Flash Vendor */
+ struct atl1_option opt = {
+ .type = range_option,
+ .name = "SPI Flash Vendor",
+ .err = "using default of "
+ __MODULE_STRING(FLASH_VENDOR_DEFAULT),
+ .def = DEFAULT_INT_MOD_CNT,
+ .arg = {.r =
+ {.min = FLASH_VENDOR_MIN,.max =
+ FLASH_VENDOR_MAX}}
+ };
+ int val;
+ if (num_flash_vendor > bd) {
+ val = flash_vendor[bd];
+ atl1_validate_option(&val, &opt);
+ adapter->hw.flash_vendor = (u8) val;
+ } else {
+ adapter->hw.flash_vendor = (u8) (opt.def);
+ }
+ }
+
+ { /* PCI MSI usage */
+
+#ifdef CONFIG_PCI_MSI
+ adapter->enable_msi = enable_msi;
+#else
+ adapter->enable_msi = 0;
+#endif
+ }
+}


2007-01-22 02:37:48

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

On Sun, 21 Jan 2007 15:07:37 -0600 Jay Cliburn wrote:

> This patch contains auxiliary C files for the Attansic L1 gigabit ethernet
> adapter driver.
>
> Signed-off-by: Jay Cliburn <[email protected]>
> Signed-off-by: Chris Snook <[email protected]>
> ---
>
> atl1_ethtool.c | 436 ++++++++++++++++++++++++++++++++++
> atl1_hw.c | 728 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> atl1_param.c | 223 +++++++++++++++++
> 3 files changed, 1387 insertions(+)
>
> diff --git a/drivers/net/atl1/atl1_ethtool.c b/drivers/net/atl1/atl1_ethtool.c
> new file mode 100644
> index 0000000..4c6e505
> --- /dev/null
> +++ b/drivers/net/atl1/atl1_ethtool.c
> @@ -0,0 +1,436 @@
> +/**

Please use "/**" _only_ to begin kernel-doc comments
(and this is not a kernel-doc comment).
(occurs at multiple other places in this and the other patches)

> + * Copyright(c) 2005 - 2006 Attansic Corporation. All rights reserved.
> + * Copyright(c) 2006 Chris Snook <[email protected]>
> + * Copyright(c) 2006 Jay Cliburn <[email protected]>
> + *
> + * Derived from Intel e1000 driver
> + * Copyright(c) 1999 - 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.
> + **/
> +
> +
> +static int atl1_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
> +{
> + struct atl1_adapter *adapter = netdev_priv(netdev);
> + struct atl1_hw *hw = &adapter->hw;
> +
> + ecmd->supported = (SUPPORTED_10baseT_Half |
> + SUPPORTED_10baseT_Full |
> + SUPPORTED_100baseT_Half |
> + SUPPORTED_100baseT_Full |
> + SUPPORTED_1000baseT_Full |
> + SUPPORTED_Autoneg | SUPPORTED_TP);
> + ecmd->advertising = ADVERTISED_TP;
> + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
> + hw->media_type == MEDIA_TYPE_1000M_FULL) {
> + ecmd->advertising |= ADVERTISED_Autoneg;
> + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR) {
> + ecmd->advertising |= ADVERTISED_Autoneg;
> + ecmd->advertising |=
> + (ADVERTISED_10baseT_Half |
> + ADVERTISED_10baseT_Full |
> + ADVERTISED_100baseT_Half |
> + ADVERTISED_100baseT_Full |
> + ADVERTISED_1000baseT_Full);
> + } else {
> + ecmd->advertising |= (ADVERTISED_1000baseT_Full);

Kernel coding style is not to use braces around one-statement "blocks."
(occurs in multiple other places)

> + }
> + }
> + ecmd->port = PORT_TP;
> + ecmd->phy_address = 0;
> + ecmd->transceiver = XCVR_INTERNAL;
> +
> + if (netif_carrier_ok(adapter->netdev)) {
> + u16 link_speed, link_duplex;
> + atl1_get_speed_and_duplex(hw, &link_speed, &link_duplex);
> + ecmd->speed = link_speed;
> + if (link_duplex == FULL_DUPLEX)
> + ecmd->duplex = DUPLEX_FULL;
> + else
> + ecmd->duplex = DUPLEX_HALF;
> + } else {
> + ecmd->speed = -1;
> + ecmd->duplex = -1;
> + }
> + if (hw->media_type == MEDIA_TYPE_AUTO_SENSOR ||
> + hw->media_type == MEDIA_TYPE_1000M_FULL) {
> + ecmd->autoneg = AUTONEG_ENABLE;
> + } else {
> + ecmd->autoneg = AUTONEG_DISABLE;
> + }
> +
> + return 0;
> +}
> +
> +static int atl1_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
> +{
> + struct atl1_adapter *adapter = netdev_priv(netdev);
> + struct atl1_hw *hw = &adapter->hw;
> + u16 phy_data;
> + int ret_val = 0;
> + u16 old_media_type = hw->media_type;
> +
> + if (netif_running(adapter->netdev)) {
> + printk(KERN_DEBUG "%s: ethtool shutting down link adapter\n",

What's a "link adapter"?

> + atl1_driver_name);
> + atl1_down(adapter);
> + }
> +
> + if (ecmd->autoneg == AUTONEG_ENABLE) {
> + hw->media_type = MEDIA_TYPE_AUTO_SENSOR;
> + } else {
> + if (ecmd->speed == SPEED_1000) {
> + if (ecmd->duplex != DUPLEX_FULL) {
> + printk(KERN_WARNING
> + "%s: can't force to 1000M half duplex\n",
> + atl1_driver_name);
> + ret_val = -EINVAL;
> + goto exit_sset;
> + }
> + hw->media_type = MEDIA_TYPE_1000M_FULL;
> + } else if (ecmd->speed == SPEED_100) {
> + if (ecmd->duplex == DUPLEX_FULL) {
> + hw->media_type = MEDIA_TYPE_100M_FULL;
> + } else {
> + hw->media_type = MEDIA_TYPE_100M_HALF;
> + }
> + } else {
> + if (ecmd->duplex == DUPLEX_FULL) {
> + hw->media_type = MEDIA_TYPE_10M_FULL;
> + } else {
> + hw->media_type = MEDIA_TYPE_10M_HALF;
> + }
> + }
> + }

...

> +}
> +
> +static void atl1_get_drvinfo(struct net_device *netdev,
> + struct ethtool_drvinfo *drvinfo)
> +{
> + struct atl1_adapter *adapter = netdev_priv(netdev);
> +
> + strncpy(drvinfo->driver, atl1_driver_name, 32);
> + strncpy(drvinfo->version, atl1_driver_version, 32);
> + strncpy(drvinfo->fw_version, "N/A", 32);
> + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);

Use sizeof(drvinfo->driver) etc. above instead of "32".

> + drvinfo->eedump_len = 48;
> +}
> +
> +

> diff --git a/drivers/net/atl1/atl1_hw.c b/drivers/net/atl1/atl1_hw.c
> new file mode 100644
> index 0000000..4062abd
> --- /dev/null
> +++ b/drivers/net/atl1/atl1_hw.c
> @@ -0,0 +1,728 @@
> +
> +extern char atl1_driver_name[];

Externs should be in header files.

> +/**
> + * Reset the transmit and receive units; mask and clear all interrupts.
> + * hw - Struct containing variables accessed by shared code
> + * return : ATL1_SUCCESS or idle status (if error)
> + **/

This function comment block is almost kernel-doc...
It would be good to make it so.

> +s32 atl1_reset_hw(struct atl1_hw * hw)

*hw)


> +{
...
> +}
> +
> +/** function about EEPROM
> + *
> + * check_eeprom_exist
> + * return 0 if eeprom exist
> + **/

Use kernel-doc format or don't begin comment block with /**.
See Documentation/kernel-doc-nano-HOWTO.txt for info.
(multiple places)

> +static int atl1_check_eeprom_exist(struct atl1_hw *hw)
> +{
> + u32 value;
> + value = ioread32(hw->hw_addr + REG_SPI_FLASH_CTRL);
> + if (value & SPI_FLASH_CTRL_EN_VPD) {
> + value &= ~SPI_FLASH_CTRL_EN_VPD;
> + iowrite32(value, hw->hw_addr + REG_SPI_FLASH_CTRL);
> + }
> +
> + value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST);
> + return ((value & 0xFF00) == 0x6C00) ? 0 : 1;

Are there defines or enums for these?
Fewer magic numbers would be nice/helpful/readable.

> +}
> +
> +static bool atl1_read_eeprom(struct atl1_hw *hw, u32 offset, u32 * p_value)

*p_value)

> +{
...
> +}
> +
> +/**
> + * Reads the value from a PHY register
> + * hw - Struct containing variables accessed by shared code
> + * reg_addr - address of the PHY register to read
> + **/

kernel-doc

> +s32 atl1_read_phy_reg(struct atl1_hw * hw, u16 reg_addr, u16 * phy_data)

CodingStyle: *hw, ... *phy_data)

> +{
...
> +}
> +
> +#define CUSTOM_SPI_CS_SETUP 2
> +#define CUSTOM_SPI_CLK_HI 2
> +#define CUSTOM_SPI_CLK_LO 2
> +#define CUSTOM_SPI_CS_HOLD 2
> +#define CUSTOM_SPI_CS_HI 3
> +
> +static bool atl1_spi_read(struct atl1_hw *hw, u32 addr, u32 * buf)
> +{
...
> +}
> +
> +/**
> + * get_permanent_address
> + * return 0 if get valid mac address,
> + **/

kernel-doc

> +int atl1_get_permanent_address(struct atl1_hw *hw)
> +{
> + u32 addr[2];
> + u32 i, control;
> + u16 reg;
> + u8 eth_addr[NODE_ADDRESS_SIZE];

Use ETH_ALEN from if_ether.h instead of your own NODE_ADDRESS_SIZE.

> + bool key_valid;
> +
> + if (is_valid_ether_addr(hw->perm_mac_addr))
> + return 0;
> + }
...
> +}
> +
> +
> +/**
> + * Make L001's PHY out of Power Saving State (bug)
> + * hw - Struct containing variables accessed by shared code
> + * when power on, L001's PHY always on Power saving State
> + * (Gigabit Link forbidden)
> + **/
> +static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw)
> +{
> + s32 ret;
> + ret = atl1_write_phy_reg(hw, 29, 0x0029);

Fewer magic numbers?

> + if (ret)
> + return ret;
> + return atl1_write_phy_reg(hw, 30, 0);
> +}

> diff --git a/drivers/net/atl1/atl1_param.c b/drivers/net/atl1/atl1_param.c
> new file mode 100644
> index 0000000..4732f43
> --- /dev/null
> +++ b/drivers/net/atl1/atl1_param.c
> @@ -0,0 +1,223 @@
> +
> +#include <linux/types.h>
> +#include <linux/pci.h>
> +#include <linux/moduleparam.h>
> +#include "atl1.h"
> +
> +extern char atl1_driver_name[];

in a header file, please.

> +/**
> + * This is the only thing that needs to be changed to adjust the
> + * maximum number of ports that the driver can manage.
> + **/
> +#define ATL1_MAX_NIC 4
> +
> +#define OPTION_UNSET -1
> +#define OPTION_DISABLED 0
> +#define OPTION_ENABLED 1
> +
> +#define ATL1_PARAM_INIT { [0 ... ATL1_MAX_NIC] = OPTION_UNSET }
> +
> +/**
> + * Interrupt Moderate Timer in units of 2 us
> + *
> + * Valid Range: 10-65535
> + *
> + * Default Value: 100 (200us)
> + **/
> +static int __devinitdata int_mod_timer[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT;
> +static int num_int_mod_timer = 0;
> +module_param_array_named(int_mod_timer, int_mod_timer, int, &num_int_mod_timer, 0);
> +MODULE_PARM_DESC(int_mod_timer, "Interrupt moderator timer");
> +
> +/**
> + * flash_vendor
> + *
> + * Valid Range: 0-2
> + *
> + * 0 - Atmel
> + * 1 - SST
> + * 2 - ST
> + *
> + * Default Value: 0
> + **/
> +static int __devinitdata flash_vendor[ATL1_MAX_NIC+1] = ATL1_PARAM_INIT;
> +static int num_flash_vendor = 0;
> +module_param_array_named(flash_vendor, flash_vendor, int, &num_flash_vendor, 0);
> +MODULE_PARM_DESC(flash_vendor, "SPI flash vendor");
> +
> +int enable_msi;
> +module_param(enable_msi, int, 0444);
> +MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");

Hm, I thought that we didn't want individual drivers having MSI config
options...

> +
> +#define DEFAULT_INT_MOD_CNT 100 /* 200us */
> +#define MAX_INT_MOD_CNT 65000
> +#define MIN_INT_MOD_CNT 50
> +
> +#define FLASH_VENDOR_DEFAULT 0
> +#define FLASH_VENDOR_MIN 0
> +#define FLASH_VENDOR_MAX 2
> +
> +
> +
> +/**
> + * atl1_check_options - Range Checking for Command Line Parameters
> + * @adapter: board private structure
> + *
> + * This routine checks all command line parameters for valid user
> + * input. If an invalid value is given, or if no user specified
> + * value exists, a default value is used. The final value is stored
> + * in a variable in the adapter structure.
> + **/

Hey, it's kernel-doc. :)

> +void __devinit atl1_check_options(struct atl1_adapter *adapter)
> +{
> +
...
> + { /* PCI MSI usage */
> +
> +#ifdef CONFIG_PCI_MSI
> + adapter->enable_msi = enable_msi;
> +#else
> + adapter->enable_msi = 0;
> +#endif
> + }
> +}
> -

---
~Randy

2007-01-22 03:33:52

by Jay Cliburn

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Randy Dunlap wrote:
> On Sun, 21 Jan 2007 15:07:37 -0600 Jay Cliburn wrote:
[snip]

>> + value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST);
>> + return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
>
> Are there defines or enums for these?
> Fewer magic numbers would be nice/helpful/readable.
[snip]
>> + s32 ret;
>> + ret = atl1_write_phy_reg(hw, 29, 0x0029);
>
> Fewer magic numbers?

Unfortunately, we don't have a spec. This is how the vendor coded it.

[snip]
>> +
>> +int enable_msi;
>> +module_param(enable_msi, int, 0444);
>> +MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");
>
> Hm, I thought that we didn't want individual drivers having MSI config
> options...

Luca? This one was yours IIRC. Care to chime in?

Randy, thank you for the review.

Jay

2007-01-22 20:00:15

by Luca Tettamanti

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Il Sun, Jan 21, 2007 at 09:33:39PM -0600, Jay Cliburn ha scritto:
> Randy Dunlap wrote:
> >On Sun, 21 Jan 2007 15:07:37 -0600 Jay Cliburn wrote:
> [snip]
>
> >>+ value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST);
> >>+ return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
> >
> >Are there defines or enums for these?
> >Fewer magic numbers would be nice/helpful/readable.
> [snip]
> >>+ s32 ret;
> >>+ ret = atl1_write_phy_reg(hw, 29, 0x0029);
> >
> >Fewer magic numbers?
>
> Unfortunately, we don't have a spec. This is how the vendor coded it.
>
> [snip]
> >>+
> >>+int enable_msi;
> >>+module_param(enable_msi, int, 0444);
> >>+MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");
> >
> >Hm, I thought that we didn't want individual drivers having MSI config
> >options...
>
> Luca? This one was yours IIRC. Care to chime in?

I remember that discussion, but since there's no sistem-wide MSI
blacklist (or whitelist) I don't think it's safe to enable it
unconditionally. Judging from bug reports on lkml it's not safe to
assume that MSI support is sane on non-Intel chipsets.

Luca
--
Il coraggio non mi manca.
E` la paura che mi frega...

2007-01-23 19:29:01

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

On Mon, 22 Jan 2007 21:00:04 +0100
Luca Tettamanti <[email protected]> wrote:

> Il Sun, Jan 21, 2007 at 09:33:39PM -0600, Jay Cliburn ha scritto:
> > Randy Dunlap wrote:
> > >On Sun, 21 Jan 2007 15:07:37 -0600 Jay Cliburn wrote:
> > [snip]
> >
> > >>+ value = ioread16(hw->hw_addr + REG_PCIE_CAP_LIST);
> > >>+ return ((value & 0xFF00) == 0x6C00) ? 0 : 1;
> > >
> > >Are there defines or enums for these?
> > >Fewer magic numbers would be nice/helpful/readable.
> > [snip]
> > >>+ s32 ret;
> > >>+ ret = atl1_write_phy_reg(hw, 29, 0x0029);
> > >
> > >Fewer magic numbers?
> >
> > Unfortunately, we don't have a spec. This is how the vendor coded it.
> >
> > [snip]
> > >>+
> > >>+int enable_msi;
> > >>+module_param(enable_msi, int, 0444);
> > >>+MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");
> > >
> > >Hm, I thought that we didn't want individual drivers having MSI config
> > >options...
> >
> > Luca? This one was yours IIRC. Care to chime in?
>
> I remember that discussion, but since there's no sistem-wide MSI
> blacklist (or whitelist) I don't think it's safe to enable it
> unconditionally. Judging from bug reports on lkml it's not safe to
> assume that MSI support is sane on non-Intel chipsets.
>
> Luca

There is MSI blacklisting see drivers/pci/quirks.c code.
But the blacklist isn't complete enough yet.

IMHO the MSI disabling should be removed from drivers and be done
in the PCI core. But it doesn't seem to have gotten widespread
support.


--
Stephen Hemminger <[email protected]>

2007-01-23 21:19:25

by Luca Tettamanti

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Il Tue, Jan 23, 2007 at 11:25:22AM -0800, Stephen Hemminger ha scritto:
> On Mon, 22 Jan 2007 21:00:04 +0100
> Luca Tettamanti <[email protected]> wrote:
>
> > Il Sun, Jan 21, 2007 at 09:33:39PM -0600, Jay Cliburn ha scritto:
> > > Randy Dunlap wrote:
> > > >On Sun, 21 Jan 2007 15:07:37 -0600 Jay Cliburn wrote:
> > > [snip]
> > > >>+
> > > >>+int enable_msi;
> > > >>+module_param(enable_msi, int, 0444);
> > > >>+MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");
> > > >
> > > >Hm, I thought that we didn't want individual drivers having MSI config
> > > >options...
> > >
> > > Luca? This one was yours IIRC. Care to chime in?
> >
> > I remember that discussion, but since there's no sistem-wide MSI
> > blacklist (or whitelist) I don't think it's safe to enable it
> > unconditionally. Judging from bug reports on lkml it's not safe to
> > assume that MSI support is sane on non-Intel chipsets.
> >
> > Luca
>
> There is MSI blacklisting see drivers/pci/quirks.c code.
> But the blacklist isn't complete enough yet.
>
> IMHO the MSI disabling should be removed from drivers and be done
> in the PCI core. But it doesn't seem to have gotten widespread
> support.

Does the INTx madness (like this one:
http://marc.theaimsgroup.com/?l=linux-kernel&m=116668921431574&w=2
) affect also PCI-E INTx emulation?

Anyway...

Unconditionally enable MSI in atl1 driver. Also remove some useless
#ifdef since pci_{en,dis}able_msi() are no-op when MSI support is not
configured in.

Signed-off-by: Luca Tettamanti <[email protected]>
---
Patch against current netdev tree.

drivers/net/atl1/atl1.h | 1 -
drivers/net/atl1/atl1_main.c | 22 +++++++---------------
drivers/net/atl1/atl1_param.c | 13 -------------
3 files changed, 7 insertions(+), 29 deletions(-)

diff --git a/drivers/net/atl1/atl1.h b/drivers/net/atl1/atl1.h
index 1d13e8f..0b30e1c 100644
--- a/drivers/net/atl1/atl1.h
+++ b/drivers/net/atl1/atl1.h
@@ -281,7 +281,6 @@ struct atl1_adapter {
struct atl1_smb smb;
struct atl1_cmb cmb;

- int enable_msi;
u32 pci_state[16];
};

diff --git a/drivers/net/atl1/atl1_main.c b/drivers/net/atl1/atl1_main.c
index c0b1555..68e6cd1 100644
--- a/drivers/net/atl1/atl1_main.c
+++ b/drivers/net/atl1/atl1_main.c
@@ -1793,18 +1793,12 @@ s32 atl1_up(struct atl1_adapter *adapter)
goto err_up;
}

-#ifdef CONFIG_PCI_MSI
- if (adapter->enable_msi) {
- err = pci_enable_msi(adapter->pdev);
- if (err) {
- dev_info(&adapter->pdev->dev, "Unable to enable MSI: %d\n", err);
- adapter->enable_msi = 0;
- }
- }
-#endif
- if (!adapter->enable_msi)
+ err = pci_enable_msi(adapter->pdev);
+ if (err) {
+ dev_info(&adapter->pdev->dev, "Unable to enable MSI: %d\n", err);
irq_flags |= IRQF_SHARED;
-
+ }
+
err = request_irq(adapter->pdev->irq, &atl1_intr, irq_flags,
netdev->name, netdev);
if (unlikely(err))
@@ -1821,6 +1815,7 @@ s32 atl1_up(struct atl1_adapter *adapter)
free_irq(adapter->pdev->irq, netdev);

err_up:
+ pci_disable_msi(adapter->pdev);
/* free rx_buffers */
atl1_clean_rx_ring(adapter);
return err;
@@ -1836,10 +1831,7 @@ void atl1_down(struct atl1_adapter *adapter)

atl1_irq_disable(adapter);
free_irq(adapter->pdev->irq, netdev);
-#ifdef CONFIG_PCI_MSI
- if (adapter->enable_msi)
- pci_disable_msi(adapter->pdev);
-#endif
+ pci_disable_msi(adapter->pdev);
atl1_reset_hw(&adapter->hw);
adapter->cmb.cmb->int_stats = 0;

diff --git a/drivers/net/atl1/atl1_param.c b/drivers/net/atl1/atl1_param.c
index 4732f43..caa2d83 100644
--- a/drivers/net/atl1/atl1_param.c
+++ b/drivers/net/atl1/atl1_param.c
@@ -68,10 +68,6 @@ static int num_flash_vendor = 0;
module_param_array_named(flash_vendor, flash_vendor, int, &num_flash_vendor, 0);
MODULE_PARM_DESC(flash_vendor, "SPI flash vendor");

-int enable_msi;
-module_param(enable_msi, int, 0444);
-MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");
-
#define DEFAULT_INT_MOD_CNT 100 /* 200us */
#define MAX_INT_MOD_CNT 65000
#define MIN_INT_MOD_CNT 50
@@ -211,13 +207,4 @@ void __devinit atl1_check_options(struct atl1_adapter *adapter)
adapter->hw.flash_vendor = (u8) (opt.def);
}
}
-
- { /* PCI MSI usage */
-
-#ifdef CONFIG_PCI_MSI
- adapter->enable_msi = enable_msi;
-#else
- adapter->enable_msi = 0;
-#endif
- }
}



Luca
--
Inquietudine sintetica
Solo, davanti all'ignoto
Tienimi stretto a te

2007-01-23 21:33:39

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Stephen Hemminger wrote:
> IMHO the MSI disabling should be removed from drivers and be done
> in the PCI core.

That is the consensus opinion.

Currently drivers implement the MSI tests because the core PCI code
hasn't been up to snuff. I (and others) have been discouraging that,
but when a user faces a choice between working and non-working network,
the pragmatic solution wins.

All efforts to get us to the point where we can remove the MSI tests
from drivers are strongly supported...

Jeff


2007-01-23 21:37:17

by Stephen Hemminger

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

On Tue, 23 Jan 2007 16:33:29 -0500
Jeff Garzik <[email protected]> wrote:

> Stephen Hemminger wrote:
> > IMHO the MSI disabling should be removed from drivers and be done
> > in the PCI core.
>
> That is the consensus opinion.
>
> Currently drivers implement the MSI tests because the core PCI code
> hasn't been up to snuff. I (and others) have been discouraging that,
> but when a user faces a choice between working and non-working network,
> the pragmatic solution wins.
>
> All efforts to get us to the point where we can remove the MSI tests
> from drivers are strongly supported...
>
> Jeff

So far, either MSI works for all devices or is broken, so it makes
sense to have a "msi=off" boot option (if there isn't already)

--
Stephen Hemminger <[email protected]>

2007-01-23 21:53:38

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Stephen Hemminger wrote:
> On Tue, 23 Jan 2007 16:33:29 -0500
> Jeff Garzik <[email protected]> wrote:
>
>> Stephen Hemminger wrote:
>>> IMHO the MSI disabling should be removed from drivers and be done
>>> in the PCI core.
>> That is the consensus opinion.
>>
>> Currently drivers implement the MSI tests because the core PCI code
>> hasn't been up to snuff. I (and others) have been discouraging that,
>> but when a user faces a choice between working and non-working network,
>> the pragmatic solution wins.

Linus's remark (IIRC) was to not enable CONFIG_PCI_MSI then.

>> All efforts to get us to the point where we can remove the MSI tests
>> from drivers are strongly supported...
>>
>> Jeff
>
> So far, either MSI works for all devices or is broken, so it makes
> sense to have a "msi=off" boot option (if there isn't already)
>
There is one, but it's spelled "pci=nomsi".

--
~Randy

2007-01-23 22:07:08

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Randy Dunlap wrote:
> Stephen Hemminger wrote:
>> On Tue, 23 Jan 2007 16:33:29 -0500
>> Jeff Garzik <[email protected]> wrote:
>>
>>> Stephen Hemminger wrote:
>>>> IMHO the MSI disabling should be removed from drivers and be done
>>>> in the PCI core.
>>> That is the consensus opinion.
>>>
>>> Currently drivers implement the MSI tests because the core PCI code
>>> hasn't been up to snuff. I (and others) have been discouraging that,
>>> but when a user faces a choice between working and non-working
>>> network, the pragmatic solution wins.
>
> Linus's remark (IIRC) was to not enable CONFIG_PCI_MSI then.

Most distros, especially cutting edge ones like Fedora, will enable
CONFIG_PCI_MSI.


>>> All efforts to get us to the point where we can remove the MSI tests
>>> from drivers are strongly supported...
>>>
>>> Jeff
>>
>> So far, either MSI works for all devices or is broken, so it makes
>> sense to have a "msi=off" boot option (if there isn't already)
>>
> There is one, but it's spelled "pci=nomsi".

Yep. Thanks for repeating this, and refreshing the collective memory.

Jeff



2007-01-27 21:02:13

by Jay Cliburn

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Luca Tettamanti wrote:
[snip]
>
> Anyway...
>
> Unconditionally enable MSI in atl1 driver. Also remove some useless
> #ifdef since pci_{en,dis}able_msi() are no-op when MSI support is not
> configured in.
>
> Signed-off-by: Luca Tettamanti <[email protected]>

Acked-by: Jay Cliburn <[email protected]>

I tested this patch today. Works fine on my Asus M2V mainboard (Via
K8T890).

Here are the eth0 entries in /proc/interrupts before and after the patch.

BEFORE: 36: 93 322091 IO-APIC-fasteoi eth0
AFTER: 2298: 85 7289 PCI-MSI-edge eth0

Jeff, shall I add this to the larger patch I'm working on for submittal
later this weekend, or do you just add it directly to netdev? (I prefer
to do the former if it's okay with you.)

Jay

> ---
> Patch against current netdev tree.
>
> drivers/net/atl1/atl1.h | 1 -
> drivers/net/atl1/atl1_main.c | 22 +++++++---------------
> drivers/net/atl1/atl1_param.c | 13 -------------
> 3 files changed, 7 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/net/atl1/atl1.h b/drivers/net/atl1/atl1.h
> index 1d13e8f..0b30e1c 100644
> --- a/drivers/net/atl1/atl1.h
> +++ b/drivers/net/atl1/atl1.h
> @@ -281,7 +281,6 @@ struct atl1_adapter {
> struct atl1_smb smb;
> struct atl1_cmb cmb;
>
> - int enable_msi;
> u32 pci_state[16];
> };
>
> diff --git a/drivers/net/atl1/atl1_main.c b/drivers/net/atl1/atl1_main.c
> index c0b1555..68e6cd1 100644
> --- a/drivers/net/atl1/atl1_main.c
> +++ b/drivers/net/atl1/atl1_main.c
> @@ -1793,18 +1793,12 @@ s32 atl1_up(struct atl1_adapter *adapter)
> goto err_up;
> }
>
> -#ifdef CONFIG_PCI_MSI
> - if (adapter->enable_msi) {
> - err = pci_enable_msi(adapter->pdev);
> - if (err) {
> - dev_info(&adapter->pdev->dev, "Unable to enable MSI: %d\n", err);
> - adapter->enable_msi = 0;
> - }
> - }
> -#endif
> - if (!adapter->enable_msi)
> + err = pci_enable_msi(adapter->pdev);
> + if (err) {
> + dev_info(&adapter->pdev->dev, "Unable to enable MSI: %d\n", err);
> irq_flags |= IRQF_SHARED;
> -
> + }
> +
> err = request_irq(adapter->pdev->irq, &atl1_intr, irq_flags,
> netdev->name, netdev);
> if (unlikely(err))
> @@ -1821,6 +1815,7 @@ s32 atl1_up(struct atl1_adapter *adapter)
> free_irq(adapter->pdev->irq, netdev);
>
> err_up:
> + pci_disable_msi(adapter->pdev);
> /* free rx_buffers */
> atl1_clean_rx_ring(adapter);
> return err;
> @@ -1836,10 +1831,7 @@ void atl1_down(struct atl1_adapter *adapter)
>
> atl1_irq_disable(adapter);
> free_irq(adapter->pdev->irq, netdev);
> -#ifdef CONFIG_PCI_MSI
> - if (adapter->enable_msi)
> - pci_disable_msi(adapter->pdev);
> -#endif
> + pci_disable_msi(adapter->pdev);
> atl1_reset_hw(&adapter->hw);
> adapter->cmb.cmb->int_stats = 0;
>
> diff --git a/drivers/net/atl1/atl1_param.c b/drivers/net/atl1/atl1_param.c
> index 4732f43..caa2d83 100644
> --- a/drivers/net/atl1/atl1_param.c
> +++ b/drivers/net/atl1/atl1_param.c
> @@ -68,10 +68,6 @@ static int num_flash_vendor = 0;
> module_param_array_named(flash_vendor, flash_vendor, int, &num_flash_vendor, 0);
> MODULE_PARM_DESC(flash_vendor, "SPI flash vendor");
>
> -int enable_msi;
> -module_param(enable_msi, int, 0444);
> -MODULE_PARM_DESC(enable_msi, "Enable PCI MSI");
> -
> #define DEFAULT_INT_MOD_CNT 100 /* 200us */
> #define MAX_INT_MOD_CNT 65000
> #define MIN_INT_MOD_CNT 50
> @@ -211,13 +207,4 @@ void __devinit atl1_check_options(struct atl1_adapter *adapter)
> adapter->hw.flash_vendor = (u8) (opt.def);
> }
> }
> -
> - { /* PCI MSI usage */
> -
> -#ifdef CONFIG_PCI_MSI
> - adapter->enable_msi = enable_msi;
> -#else
> - adapter->enable_msi = 0;
> -#endif
> - }
> }

2007-01-27 21:16:10

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Jay Cliburn wrote:
> Jeff, shall I add this to the larger patch I'm working on for submittal
> later this weekend, or do you just add it directly to netdev? (I prefer
> to do the former if it's okay with you.)

As a driver maintainer, you need to patch sets, and submit them in a
timely fashion to me. Note I said patch set, not patch, in following
with Rule #3 from Documentation/SubmittingPatches. Also make sure to
review http://linux.yyz.us/patch-format.html

Jeff


2007-01-27 22:05:26

by Jay Cliburn

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Jeff Garzik wrote:
> As a driver maintainer, you need to patch sets, and submit them in a
> timely fashion to me. Note I said patch set, not patch, in following
> with Rule #3 from Documentation/SubmittingPatches. Also make sure to
> review http://linux.yyz.us/patch-format.html

Understood. Both references reviewed. Thanks.

Sorry, but one last question... These two patches generated overnight
by Andrew:

Message-Id: <[email protected]>
Subject: + git-netdev-all-atl1-pm-fix.patch added to -mm tree

and

Message-Id: <[email protected]>
Subject: + git-netdev-all-atl1-build-fix.patch added to -mm tree

Do I include these in my patch set that I submit to you, or do you apply
them to netdev directly?

Jay


2007-01-27 22:38:50

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCH 4/4] atl1: Ancillary C files for Attansic L1 driver

Jay Cliburn wrote:
> Jeff Garzik wrote:
>> As a driver maintainer, you need to patch sets, and submit them in a
>> timely fashion to me. Note I said patch set, not patch, in following
>> with Rule #3 from Documentation/SubmittingPatches. Also make sure to
>> review http://linux.yyz.us/patch-format.html
>
> Understood. Both references reviewed. Thanks.
>
> Sorry, but one last question... These two patches generated overnight
> by Andrew:
>
> Message-Id: <[email protected]>
> Subject: + git-netdev-all-atl1-pm-fix.patch added to -mm tree
>
> and
>
> Message-Id: <[email protected]>
> Subject: + git-netdev-all-atl1-build-fix.patch added to -mm tree
>
> Do I include these in my patch set that I submit to you, or do you apply
> them to netdev directly?

There is no hard and fast rule. If the patch is obvious and submitted
in correct form (Andrew's notifications are not in such a form), then I
might go ahead and apply it. But I usually reply to the patch with
"applied" if so.

In general, you can answer this question yourself. Look at
netdev-2.6.git#atl1 and see what's in there.

Jeff