#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.3"
+#define DRV_VERSION "1.4"
#define PFX DRV_NAME " "
/*
return v;
}
-static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state)
+static void 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);
break;
default:
printk(KERN_ERR PFX "Unknown power state %d\n", state);
- ret = -1;
}
sky2_pci_write16(hw, 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)
struct sky2_hw *hw = sky2->hw;
unsigned rxq = rxqaddr[sky2->port];
int i;
+ unsigned thresh;
sky2->rx_put = sky2->rx_next = 0;
sky2_qset(hw, rxq);
sky2_rx_add(sky2, re->mapaddr);
}
- /* Truncate oversize frames */
- sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), sky2->rx_bufsize - 8);
- sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON);
+
+ /*
+ * The receiver hangs if it receives frames larger than the
+ * packet buffer. As a workaround, truncate oversize frames, but
+ * the register is limited to 9 bits, so if you do frames > 2052
+ * you better get the MTU right!
+ */
+ thresh = (sky2->rx_bufsize - 8) / sizeof(u32);
+ if (thresh > 0x1ff)
+ sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_OFF);
+ else {
+ sky2_write16(hw, SK_REG(sky2->port, RX_GMF_TR_THR), thresh);
+ sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON);
+ }
+
/* Tell chip about available buffers */
sky2_write16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX), sky2->rx_put);
}
}
+/* Is status ring empty or is there more to do? */
+static inline int sky2_more_work(const struct sky2_hw *hw)
+{
+ return (hw->st_idx != sky2_read16(hw, STAT_PUT_IDX));
+}
+
/* Process status response ring */
static int sky2_status_intr(struct sky2_hw *hw, int to_do)
{
/* If idle then force a fake soft NAPI poll once a second
* to work around cases where sharing an edge triggered interrupt.
*/
+static inline void sky2_idle_start(struct sky2_hw *hw)
+{
+ if (idle_timeout > 0)
+ mod_timer(&hw->idle_timer,
+ jiffies + msecs_to_jiffies(idle_timeout));
+}
+
static void sky2_idle(unsigned long arg)
{
struct sky2_hw *hw = (struct sky2_hw *) arg;
int work_done = 0;
u32 status = sky2_read32(hw, B0_Y2_SP_EISR);
+ if (!~status)
+ goto out;
+
if (status & Y2_IS_HW_ERR)
sky2_hw_intr(hw);
if (status & Y2_IS_CHK_TXA2)
sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2);
- if (status & Y2_IS_STAT_BMU)
- sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
-
work_done = sky2_status_intr(hw, work_limit);
*budget -= work_done;
dev0->quota -= work_done;
- if (work_done >= work_limit)
- return 1;
+ if (status & Y2_IS_STAT_BMU)
+ sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ);
+ if (sky2_more_work(hw))
+ return 1;
+out:
netif_rx_complete(dev0);
- status = sky2_read32(hw, B0_Y2_SP_LISR);
+ sky2_read32(hw, B0_Y2_SP_LISR);
return 0;
}
sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw);
- if (idle_timeout > 0)
- mod_timer(&hw->idle_timer,
- jiffies + msecs_to_jiffies(idle_timeout));
+ sky2_idle_start(hw);
pci_set_drvdata(pdev, hw);
{
struct sky2_hw *hw = pci_get_drvdata(pdev);
int i;
+ pci_power_t pstate = pci_choose_state(pdev, state);
- for (i = 0; i < 2; i++) {
+ if (!(pstate == PCI_D3hot || pstate == PCI_D3cold))
+ return -EINVAL;
+
+ del_timer_sync(&hw->idle_timer);
+
+ for (i = 0; i < hw->ports; i++) {
struct net_device *dev = hw->dev[i];
if (dev) {
}
}
- return sky2_set_power_state(hw, pci_choose_state(pdev, state));
+ sky2_write32(hw, B0_IMSK, 0);
+ pci_save_state(pdev);
+ sky2_set_power_state(hw, pstate);
+ return 0;
}
static int sky2_resume(struct pci_dev *pdev)
pci_restore_state(pdev);
pci_enable_wake(pdev, PCI_D0, 0);
- err = sky2_set_power_state(hw, PCI_D0);
- if (err)
- goto out;
+ sky2_set_power_state(hw, PCI_D0);
err = sky2_reset(hw);
if (err)
goto out;
- for (i = 0; i < 2; i++) {
+ sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
+
+ for (i = 0; i < hw->ports; i++) {
struct net_device *dev = hw->dev[i];
if (dev && netif_running(dev)) {
netif_device_attach(dev);
printk(KERN_ERR PFX "%s: could not up: %d\n",
dev->name, err);
dev_close(dev);
- break;
+ goto out;
}
}
}
+
+ sky2_idle_start(hw);
out:
return err;
}