*/
#include <linux/config.h>
+#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include "skge.h"
#define DRV_NAME "skge"
-#define DRV_VERSION "1.1"
+#define DRV_VERSION "1.3"
#define PFX DRV_NAME " "
#define DEFAULT_TX_RING_SIZE 128
static int skge_up(struct net_device *dev);
static int skge_down(struct net_device *dev);
+static void skge_phy_reset(struct skge_port *skge);
static void skge_tx_clean(struct skge_port *skge);
static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
static int gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val);
static void genesis_get_stats(struct skge_port *skge, u64 *data);
static void yukon_get_stats(struct skge_port *skge, u64 *data);
static void yukon_init(struct skge_hw *hw, int port);
-static void yukon_reset(struct skge_hw *hw, int port);
static void genesis_mac_init(struct skge_hw *hw, int port);
-static void genesis_reset(struct skge_hw *hw, int port);
static void genesis_link_up(struct skge_port *skge);
/* Avoid conditionals by using array */
regs->len - B3_RI_WTO_R1);
}
-/* Wake on Lan only supported on Yukon chps with rev 1 or above */
+/* Wake on Lan only supported on Yukon chips with rev 1 or above */
static int wol_supported(const struct skge_hw *hw)
{
return !((hw->chip_id == CHIP_ID_GENESIS ||
return 0;
}
-/* Determine supported/adverised modes based on hardware.
- * Note: ethtoool ADVERTISED_xxx == SUPPORTED_xxx
+/* Determine supported/advertised modes based on hardware.
+ * Note: ethtool ADVERTISED_xxx == SUPPORTED_xxx
*/
static u32 skge_supported_modes(const struct skge_hw *hw)
{
skge->autoneg = ecmd->autoneg;
skge->advertising = ecmd->advertising;
- if (netif_running(dev)) {
- skge_down(dev);
- skge_up(dev);
- }
+ if (netif_running(dev))
+ skge_phy_reset(skge);
+
return (0);
}
struct ethtool_ringparam *p)
{
struct skge_port *skge = netdev_priv(dev);
+ int err;
if (p->rx_pending == 0 || p->rx_pending > MAX_RX_RING_SIZE ||
p->tx_pending == 0 || p->tx_pending > MAX_TX_RING_SIZE)
if (netif_running(dev)) {
skge_down(dev);
- skge_up(dev);
+ err = skge_up(dev);
+ if (err)
+ dev_close(dev);
}
return 0;
static int skge_nway_reset(struct net_device *dev)
{
struct skge_port *skge = netdev_priv(dev);
- struct skge_hw *hw = skge->hw;
- int port = skge->port;
if (skge->autoneg != AUTONEG_ENABLE || !netif_running(dev))
return -EINVAL;
- spin_lock_bh(&hw->phy_lock);
- if (hw->chip_id == CHIP_ID_GENESIS) {
- genesis_reset(hw, port);
- genesis_mac_init(hw, port);
- } else {
- yukon_reset(hw, port);
- yukon_init(hw, port);
- }
- spin_unlock_bh(&hw->phy_lock);
+ skge_phy_reset(skge);
return 0;
}
else
skge->flow_control = FLOW_MODE_NONE;
- if (netif_running(dev)) {
- skge_down(dev);
- skge_up(dev);
- }
+ if (netif_running(dev))
+ skge_phy_reset(skge);
return 0;
}
return 78215; /* or: 78.125 MHz */
}
-/* Chip hz to microseconds */
+/* Chip HZ to microseconds */
static inline u32 skge_clk2usec(const struct skge_hw *hw, u32 ticks)
{
return (ticks * 1000) / hwkhz(hw);
}
-/* Microseconds to chip hz */
+/* Microseconds to chip HZ */
static inline u32 skge_usecs2clk(const struct skge_hw *hw, u32 usec)
{
return hwkhz(hw) * usec / 1000;
xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, ext);
xm_phy_write(hw, port, PHY_BCOM_CTRL, ctl);
- /* Use link status change interrrupt */
+ /* Use link status change interrupt */
xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
bcom_check_link(hw, port);
skge_write32(hw, B2_GP_IO, r);
skge_read32(hw, B2_GP_IO);
- /* Enable GMII interfac */
+ /* Enable GMII interface */
xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
bcom_phy_init(skge, jumbo);
* that jumbo frames larger than 8192 bytes will be
* truncated. Disabling all bad frame filtering causes
* the RX FIFO to operate in streaming mode, in which
- * case the XMAC will start transfering frames out of the
+ * case the XMAC will start transferring frames out of the
* RX FIFO as soon as the FIFO threshold is reached.
*/
xm_write32(hw, port, XM_MODE, XM_DEF_MODE);
port == 0 ? PA_CLR_TO_TX1 : PA_CLR_TO_TX2);
/*
- * If the transfer stucks at the MAC the STOP command will not
+ * If the transfer sticks at the MAC the STOP command will not
* terminate if we don't flush the XMAC's transmit FIFO !
*/
xm_write32(hw, port, XM_MODE,
return v;
}
-/* Marvell Phy Initailization */
+/* Marvell Phy Initialization */
static void yukon_init(struct skge_hw *hw, int port)
{
struct skge_port *skge = netdev_priv(hw->dev[port]);
skge_write16(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON);
}
+/* Go into power down mode */
+static void yukon_suspend(struct skge_hw *hw, int port)
+{
+ u16 ctrl;
+
+ ctrl = gm_phy_read(hw, port, PHY_MARV_PHY_CTRL);
+ ctrl |= PHY_M_PC_POL_R_DIS;
+ gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, ctrl);
+
+ ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+ ctrl |= PHY_CT_RESET;
+ gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+
+ /* switch IEEE compatible power down mode on */
+ ctrl = gm_phy_read(hw, port, PHY_MARV_CTRL);
+ ctrl |= PHY_CT_PDOWN;
+ gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl);
+}
+
static void yukon_stop(struct skge_port *skge)
{
struct skge_hw *hw = skge->hw;
& ~(GM_GPCR_TX_ENA|GM_GPCR_RX_ENA));
gma_read16(hw, port, GM_GP_CTRL);
- if (hw->chip_id == CHIP_ID_YUKON_LITE &&
- hw->chip_rev >= CHIP_REV_YU_LITE_A3) {
- u32 io = skge_read32(hw, B2_GP_IO);
-
- io |= GP_DIR_9 | GP_IO_9;
- skge_write32(hw, B2_GP_IO, io);
- skge_read32(hw, B2_GP_IO);
- }
+ yukon_suspend(hw, port);
/* set GPHY Control reset */
skge_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_SET);
/* XXX restart autonegotiation? */
}
+static void skge_phy_reset(struct skge_port *skge)
+{
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+
+ netif_stop_queue(skge->netdev);
+ netif_carrier_off(skge->netdev);
+
+ spin_lock_bh(&hw->phy_lock);
+ if (hw->chip_id == CHIP_ID_GENESIS) {
+ genesis_reset(hw, port);
+ genesis_mac_init(hw, port);
+ } else {
+ yukon_reset(hw, port);
+ yukon_init(hw, port);
+ }
+ spin_unlock_bh(&hw->phy_lock);
+}
+
/* Basic MII support */
static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
hw->intr_mask |= portirqmask[port];
skge_write32(hw, B0_IMSK, hw->intr_mask);
- /* Initialze MAC */
+ /* Initialize MAC */
spin_lock_bh(&hw->phy_lock);
if (hw->chip_id == CHIP_ID_GENESIS)
genesis_mac_init(hw, port);
kfree(skge->rx_ring.start);
free_pci_mem:
pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma);
+ skge->mem = NULL;
return err;
}
struct skge_hw *hw = skge->hw;
int port = skge->port;
+ if (skge->mem == NULL)
+ return 0;
+
if (netif_msg_ifdown(skge))
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
kfree(skge->rx_ring.start);
kfree(skge->tx_ring.start);
pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma);
+ skge->mem = NULL;
return 0;
}
}
if (unlikely(skge->tx_avail < skb_shinfo(skb)->nr_frags +1)) {
- netif_stop_queue(dev);
- spin_unlock_irqrestore(&skge->tx_lock, flags);
+ if (!netif_queue_stopped(dev)) {
+ netif_stop_queue(dev);
- printk(KERN_WARNING PFX "%s: ring full when queue awake!\n",
- dev->name);
+ printk(KERN_WARNING PFX "%s: ring full when queue awake!\n",
+ dev->name);
+ }
+ spin_unlock_irqrestore(&skge->tx_lock, flags);
return NETDEV_TX_BUSY;
}
td->dma_hi = map >> 32;
if (skb->ip_summed == CHECKSUM_HW) {
- const struct iphdr *ip
- = (const struct iphdr *) (skb->data + ETH_HLEN);
int offset = skb->h.raw - skb->data;
/* This seems backwards, but it is what the sk98lin
* does. Looks like hardware is wrong?
*/
- if (ip->protocol == IPPROTO_UDP
+ if (skb->h.ipiph->protocol == IPPROTO_UDP
&& hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON)
control = BMU_TCP_CHECK;
else
static int skge_change_mtu(struct net_device *dev, int new_mtu)
{
- int err = 0;
- int running = netif_running(dev);
+ int err;
if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU)
return -EINVAL;
+ if (!netif_running(dev)) {
+ dev->mtu = new_mtu;
+ return 0;
+ }
+
+ skge_down(dev);
- if (running)
- skge_down(dev);
dev->mtu = new_mtu;
- if (running)
- skge_up(dev);
+
+ err = skge_up(dev);
+ if (err)
+ dev_close(dev);
return err;
}
reg = gma_read16(hw, port, GM_RX_CTRL);
reg |= GM_RXCR_UCF_ENA;
- if (dev->flags & IFF_PROMISC) /* promiscious */
+ if (dev->flags & IFF_PROMISC) /* promiscuous */
reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA);
else if (dev->flags & IFF_ALLMULTI) /* all multicast */
memset(filter, 0xff, sizeof(filter));
unsigned int to_do = min(dev->quota, *budget);
unsigned int work_done = 0;
- for (e = ring->to_clean; work_done < to_do; e = e->next) {
+ for (e = ring->to_clean; prefetch(e->next), work_done < to_do; e = e->next) {
struct skge_rx_desc *rd = e->desc;
struct sk_buff *skb;
u32 control;
if (work_done >= to_do)
return 1; /* not done */
- local_irq_disable();
- __netif_rx_complete(dev);
+ netif_rx_complete(dev);
hw->intr_mask |= portirqmask[skge->port];
skge_write32(hw, B0_IMSK, hw->intr_mask);
- local_irq_enable();
+ skge_read32(hw, B0_IMSK);
+
return 0;
}
struct skge_element *e;
spin_lock(&skge->tx_lock);
- for (e = ring->to_clean; e != ring->to_use; e = e->next) {
+ for (e = ring->to_clean; prefetch(e->next), e != ring->to_use; e = e->next) {
struct skge_tx_desc *td = e->desc;
u32 control;
}
/*
- * Interrrupt from PHY are handled in tasklet (soft irq)
+ * Interrupt from PHY are handled in tasklet (soft irq)
* because accessing phy registers requires spin wait which might
* cause excess interrupt latency.
*/
local_irq_enable();
}
+static inline void skge_wakeup(struct net_device *dev)
+{
+ struct skge_port *skge = netdev_priv(dev);
+
+ prefetch(skge->rx_ring.to_clean);
+ netif_rx_schedule(dev);
+}
+
static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs)
{
struct skge_hw *hw = dev_id;
status &= hw->intr_mask;
if (status & IS_R1_F) {
hw->intr_mask &= ~IS_R1_F;
- netif_rx_schedule(hw->dev[0]);
+ skge_wakeup(hw->dev[0]);
}
if (status & IS_R2_F) {
hw->intr_mask &= ~IS_R2_F;
- netif_rx_schedule(hw->dev[1]);
+ skge_wakeup(hw->dev[1]);
}
if (status & IS_XA1_F)
}
#ifdef __BIG_ENDIAN
- /* byte swap decriptors in hardware */
+ /* byte swap descriptors in hardware */
{
u32 reg;
if (err)
goto err_out_free_irq;
- printk(KERN_INFO PFX "addr 0x%lx irq %d chip %s rev %d\n",
+ printk(KERN_INFO PFX DRV_VERSION " addr 0x%lx irq %d chip %s rev %d\n",
pci_resource_start(pdev, 0), pdev->irq,
skge_board_name(hw), hw->chip_rev);
struct net_device *dev = hw->dev[i];
if (dev) {
netif_device_attach(dev);
- if (netif_running(dev))
- skge_up(dev);
+ if (netif_running(dev) && skge_up(dev))
+ dev_close(dev);
}
}
return 0;