From 977bdf06ca8dd7ed081fab8d30249d9e6b1c24d3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 11:44:58 -0800 Subject: [PATCH] sky2: yukon-ec-u chipset initialization Add more complete setup code for Yukon EC_U chipset. Based on matching code in 8.31 code in SysKonnect vendor driver. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 50 ++++++++++++++++++++++++++++++++++++--------- drivers/net/sky2.h | 59 +++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 90 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index bfeba5b9cd7a70..ce135b84a54c86 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -232,7 +232,17 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) if (hw->ports > 1) reg1 |= PCI_Y2_PHY2_COMA; } + + if (hw->chip_id == CHIP_ID_YUKON_EC_U) { + pci_write_config_dword(hw->pdev, PCI_DEV_REG3, 0); + pci_read_config_dword(hw->pdev, PCI_DEV_REG4, ®1); + reg1 &= P_ASPM_CONTROL_MSK; + pci_write_config_dword(hw->pdev, PCI_DEV_REG4, reg1); + pci_write_config_dword(hw->pdev, PCI_DEV_REG5, 0); + } + pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1); + break; case PCI_D3hot: @@ -463,16 +473,31 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ledover |= PHY_M_LED_MO_RX(MO_LED_OFF); } - gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); + if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev >= 2) { + /* apply fixes in PHY AFE */ + gm_phy_write(hw, port, 22, 255); + /* increase differential signal amplitude in 10BASE-T */ + gm_phy_write(hw, port, 24, 0xaa99); + gm_phy_write(hw, port, 23, 0x2011); - 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); - } + /* fix for IEEE A/B Symmetry failure in 1000BASE-T */ + gm_phy_write(hw, port, 24, 0xa204); + gm_phy_write(hw, port, 23, 0x2002); - if (ledover) - gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); + /* set page register to 0 */ + gm_phy_write(hw, port, 22, 0); + } else { + 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); @@ -953,6 +978,12 @@ static int sky2_rx_start(struct sky2_port *sky2) sky2->rx_put = sky2->rx_next = 0; sky2_qset(hw, rxq); + + if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev >= 2) { + /* MAC Rx RAM Read is controlled by hardware */ + sky2_write32(hw, Q_ADDR(rxq, Q_F), F_M_RX_RAM_DIS); + } + sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1); rx_set_checksum(sky2); @@ -1035,9 +1066,10 @@ static int sky2_up(struct net_device *dev) RB_RST_SET); sky2_qset(hw, txqaddr[port]); - if (hw->chip_id == CHIP_ID_YUKON_EC_U) - sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), 0x1a0); + /* Set almost empty threshold */ + if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev == 1) + sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), 0x1a0); sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, TX_RING_SIZE - 1); diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index fd12c289a2387d..d1c71f50d8b9f6 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -5,14 +5,22 @@ #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) +enum { + PCI_DEV_REG1 = 0x40, + PCI_DEV_REG2 = 0x44, + PCI_DEV_STATUS = 0x7c, + PCI_DEV_REG3 = 0x80, + PCI_DEV_REG4 = 0x84, + PCI_DEV_REG5 = 0x88, +}; -#define PEX_LNK_STAT 0xf2 -#define PEX_UNC_ERR_STAT 0x104 -#define PEX_DEV_CTRL 0xe8 +enum { + PEX_DEV_CAP = 0xe4, + PEX_DEV_CTRL = 0xe8, + PEX_DEV_STA = 0xea, + PEX_LNK_STAT = 0xf2, + PEX_UNC_ERR_STAT= 0x104, +}; /* Yukon-2 */ enum pci_dev_reg_1 { @@ -37,6 +45,25 @@ enum pci_dev_reg_2 { PCI_USEDATA64 = 1<<0, /* Use 64Bit Data bus ext */ }; +/* PCI_OUR_REG_4 32 bit Our Register 4 (Yukon-ECU only) */ +enum pci_dev_reg_4 { + /* (Link Training & Status State Machine) */ + P_TIMER_VALUE_MSK = 0xffL<<16, /* Bit 23..16: Timer Value Mask */ + /* (Active State Power Management) */ + P_FORCE_ASPM_REQUEST = 1<<15, /* Force ASPM Request (A1 only) */ + P_ASPM_GPHY_LINK_DOWN = 1<<14, /* GPHY Link Down (A1 only) */ + P_ASPM_INT_FIFO_EMPTY = 1<<13, /* Internal FIFO Empty (A1 only) */ + P_ASPM_CLKRUN_REQUEST = 1<<12, /* CLKRUN Request (A1 only) */ + + P_ASPM_FORCE_CLKREQ_ENA = 1<<4, /* Force CLKREQ Enable (A1b only) */ + P_ASPM_CLKREQ_PAD_CTL = 1<<3, /* CLKREQ PAD Control (A1 only) */ + P_ASPM_A1_MODE_SELECT = 1<<2, /* A1 Mode Select (A1 only) */ + P_CLK_GATE_PEX_UNIT_ENA = 1<<1, /* Enable Gate PEX Unit Clock */ + P_CLK_GATE_ROOT_COR_ENA = 1<<0, /* Enable Gate Root Core Clock */ + P_ASPM_CONTROL_MSK = P_FORCE_ASPM_REQUEST | P_ASPM_GPHY_LINK_DOWN + | P_ASPM_CLKRUN_REQUEST | P_ASPM_INT_FIFO_EMPTY, +}; + #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ PCI_STATUS_SIG_SYSTEM_ERROR | \ @@ -507,6 +534,16 @@ enum { }; #define Q_ADDR(reg, offs) (B8_Q_REGS + (reg) + (offs)) +/* Q_F 32 bit Flag Register */ +enum { + F_ALM_FULL = 1<<27, /* Rx FIFO: almost full */ + F_EMPTY = 1<<27, /* Tx FIFO: empty flag */ + F_FIFO_EOF = 1<<26, /* Tag (EOF Flag) bit in FIFO */ + F_WM_REACHED = 1<<25, /* Watermark reached */ + F_M_RX_RAM_DIS = 1<<24, /* MAC Rx RAM Read Port disable */ + F_FIFO_LEVEL = 0x1fL<<16, /* Bit 23..16: # of Qwords in FIFO */ + F_WATER_MARK = 0x0007ffL, /* Bit 10.. 0: Watermark */ +}; /* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/ enum { @@ -909,10 +946,12 @@ enum { PHY_BCOM_ID1_C0 = 0x6044, PHY_BCOM_ID1_C5 = 0x6047, - PHY_MARV_ID1_B0 = 0x0C23, /* Yukon (PHY 88E1011) */ + 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) */ + PHY_MARV_ID1_C2 = 0x0CC2, /* Yukon-EC (PHY 88E1111) */ + PHY_MARV_ID1_Y2 = 0x0C91, /* Yukon-2 (PHY 88E1112) */ + PHY_MARV_ID1_FE = 0x0C83, /* Yukon-FE (PHY 88E3082 Rev.A1) */ + PHY_MARV_ID1_ECU= 0x0CB0, /* Yukon-ECU (PHY 88E1149 Rev.B2?) */ }; /* Advertisement register bits */ -- cgit 1.2.3-korg From ff81fbbe321c3a468b6225c673ca57efa501fbed Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 11:44:59 -0800 Subject: [PATCH] sky2: limit coalescing values to ring size Don't allow coalescing values to be bigger than the transmit ring. Since if you set them that big, the interrupt never happens and driver livelocks. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index ce135b84a54c86..cf7fd28d67767e 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2884,11 +2884,11 @@ static int sky2_set_coalesce(struct net_device *dev, (ecmd->rx_coalesce_usecs_irq < tmin || ecmd->rx_coalesce_usecs_irq > tmax)) return -EINVAL; - if (ecmd->tx_max_coalesced_frames > 0xffff) + if (ecmd->tx_max_coalesced_frames >= TX_RING_SIZE-1) return -EINVAL; - if (ecmd->rx_max_coalesced_frames > 0xff) + if (ecmd->rx_max_coalesced_frames > RX_MAX_PENDING) return -EINVAL; - if (ecmd->rx_max_coalesced_frames_irq > 0xff) + if (ecmd->rx_max_coalesced_frames_irq >RX_MAX_PENDING) return -EINVAL; if (ecmd->tx_coalesce_usecs == 0) -- cgit 1.2.3-korg From a8fd6266dafd564bae6758cb78c8c152e7d4115e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 11:45:00 -0800 Subject: [PATCH] sky2: poke coalescing timer to fix hang Need to restart the interrupt coalescing timer after clearing the interrupt, to avoid races with interrupt timer and processing. Patch from Carl-Daniel Halfinger Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index cf7fd28d67767e..629809433cb38f 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1895,6 +1895,17 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); + /* + * Kick the STAT_LEV_TIMER_CTRL timer. + * This fixes my hangs on Yukon-EC (0xb6) rev 1. + * The if clause is there to start the timer only if it has been + * configured correctly and not been disabled via ethtool. + */ + if (sky2_read8(hw, STAT_LEV_TIMER_CTRL) == TIM_START) { + sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_STOP); + sky2_write8(hw, STAT_LEV_TIMER_CTRL, TIM_START); + } + hwidx = sky2_read16(hw, STAT_PUT_IDX); BUG_ON(hwidx >= STATUS_RING_SIZE); rmb(); -- cgit 1.2.3-korg From 9a6d343188f5f1e9537e700fc4139c2d905ff129 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 11:45:01 -0800 Subject: [PATCH] sky2: force early transmit status Need to force a transmit coalesce timer restart after processing transmit packets. Otherwise, can get transmit status after last update and chip doesn't send the next one. Can go with the chip defaults for coalescing timers, except for Tx timer which needs to be bigger. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 629809433cb38f..d913d3407e9005 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1988,13 +1988,12 @@ exit_loop: sky2_tx_check(hw, 0, tx_done[0]); sky2_tx_check(hw, 1, tx_done[1]); - if (likely(work_done < to_do)) { - /* need to restart TX timer */ - if (is_ec_a1(hw)) { - sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); - sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); - } + if (sky2_read8(hw, STAT_TX_TIMER_CTRL) == TIM_START) { + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_STOP); + sky2_write8(hw, STAT_TX_TIMER_CTRL, TIM_START); + } + if (likely(work_done < to_do)) { netif_rx_complete(dev0); hw->intr_mask |= Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); @@ -2352,8 +2351,7 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, STAT_FIFO_ISR_WM, 16); sky2_write32(hw, STAT_TX_TIMER_INI, sky2_us2clk(hw, 1000)); - sky2_write32(hw, STAT_LEV_TIMER_INI, sky2_us2clk(hw, 100)); - sky2_write32(hw, STAT_ISR_TIMER_INI, sky2_us2clk(hw, 20)); + sky2_write32(hw, STAT_ISR_TIMER_INI, sky2_us2clk(hw, 7)); } /* enable status unit */ -- cgit 1.2.3-korg From 56a645cc1bc16ab33b33a3e0854a46c5d2c864f3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 11:45:02 -0800 Subject: [PATCH] sky2: use device iomem to access PCI config To avoid problems with PCI config access without ACPI (or busted ACPI tables), use the device's window into PCI config space. I know this probably will upset the purists, but I would rather have users than ACPI testers. It also generates less code. Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 81 ++++++++++++++++++++++-------------------------------- drivers/net/sky2.h | 21 ++++++++++++++ 2 files changed, 54 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index d913d3407e9005..fbbc85532c2e41 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -195,11 +195,11 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) 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); + power_control = sky2_pci_read16(hw, hw->pm_cap + PCI_PM_PMC); vaux = (sky2_read16(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 = sky2_pci_read16(hw, hw->pm_cap + PCI_PM_CTRL); power_control |= PCI_PM_CTRL_PME_STATUS; power_control &= ~(PCI_PM_CTRL_STATE_MASK); @@ -223,7 +223,7 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) sky2_write8(hw, B2_Y2_CLK_GATE, 0); /* Turn off phy power saving */ - pci_read_config_dword(hw->pdev, PCI_DEV_REG1, ®1); + reg1 = sky2_pci_read32(hw, PCI_DEV_REG1); reg1 &= ~(PCI_Y2_PHY1_POWD | PCI_Y2_PHY2_POWD); /* looks like this XL is back asswards .. */ @@ -234,26 +234,26 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) } if (hw->chip_id == CHIP_ID_YUKON_EC_U) { - pci_write_config_dword(hw->pdev, PCI_DEV_REG3, 0); - pci_read_config_dword(hw->pdev, PCI_DEV_REG4, ®1); + sky2_pci_write32(hw, PCI_DEV_REG3, 0); + reg1 = sky2_pci_read32(hw, PCI_DEV_REG4); reg1 &= P_ASPM_CONTROL_MSK; - pci_write_config_dword(hw->pdev, PCI_DEV_REG4, reg1); - pci_write_config_dword(hw->pdev, PCI_DEV_REG5, 0); + sky2_pci_write32(hw, PCI_DEV_REG4, reg1); + sky2_pci_write32(hw, PCI_DEV_REG5, 0); } - pci_write_config_dword(hw->pdev, PCI_DEV_REG1, reg1); + sky2_pci_write32(hw, 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); + reg1 = sky2_pci_read32(hw, PCI_DEV_REG1); 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); + sky2_pci_write32(hw, PCI_DEV_REG1, reg1); if (hw->chip_id == CHIP_ID_YUKON_XL && hw->chip_rev > 1) sky2_write8(hw, B2_Y2_CLK_GATE, 0); @@ -275,7 +275,7 @@ static int sky2_set_power_state(struct sky2_hw *hw, pci_power_t state) ret = -1; } - pci_write_config_byte(hw->pdev, hw->pm_cap + PCI_PM_CTRL, power_control); + sky2_pci_write16(hw, hw->pm_cap + PCI_PM_CTRL, power_control); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); return ret; } @@ -2059,13 +2059,13 @@ static void sky2_hw_intr(struct sky2_hw *hw) if (status & (Y2_IS_MST_ERR | Y2_IS_IRQ_STAT)) { u16 pci_err; - pci_read_config_word(hw->pdev, PCI_STATUS, &pci_err); + pci_err = sky2_pci_read16(hw, PCI_STATUS); if (net_ratelimit()) 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, + sky2_pci_write16(hw, PCI_STATUS, pci_err | PCI_STATUS_ERROR_BITS); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); } @@ -2074,7 +2074,7 @@ static void sky2_hw_intr(struct sky2_hw *hw) /* PCI-Express uncorrectable Error occurred */ u32 pex_err; - pci_read_config_dword(hw->pdev, PEX_UNC_ERR_STAT, &pex_err); + pex_err = sky2_pci_read32(hw, PEX_UNC_ERR_STAT); if (net_ratelimit()) printk(KERN_ERR PFX "%s: pci express error (0x%x)\n", @@ -2082,7 +2082,7 @@ static void sky2_hw_intr(struct sky2_hw *hw) /* clear the interrupt */ sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT, + sky2_pci_write32(hw, PEX_UNC_ERR_STAT, 0xffffffffUL); sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); @@ -2212,7 +2212,7 @@ static int sky2_reset(struct sky2_hw *hw) { u16 status; u8 t8, pmd_type; - int i, err; + int i; sky2_write8(hw, B0_CTST, CS_RST_CLR); @@ -2234,25 +2234,18 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, B0_CTST, CS_RST_CLR); /* clear PCI errors, if any */ - err = pci_read_config_word(hw->pdev, PCI_STATUS, &status); - if (err) - goto pci_err; + status = sky2_pci_read16(hw, PCI_STATUS); sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); - err = pci_write_config_word(hw->pdev, PCI_STATUS, - status | PCI_STATUS_ERROR_BITS); - if (err) - goto pci_err; + sky2_pci_write16(hw, PCI_STATUS, status | PCI_STATUS_ERROR_BITS); + sky2_write8(hw, B0_CTST, CS_MRST_CLR); /* clear any PEX errors */ - if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP)) { - err = pci_write_config_dword(hw->pdev, PEX_UNC_ERR_STAT, - 0xffffffffUL); - if (err) - goto pci_err; - } + if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP)) + sky2_pci_write32(hw, PEX_UNC_ERR_STAT, 0xffffffffUL); + pmd_type = sky2_read8(hw, B2_PMD_TYP); hw->copper = !(pmd_type == 'L' || pmd_type == 'S'); @@ -2362,14 +2355,6 @@ static int sky2_reset(struct sky2_hw *hw) sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START); return 0; - -pci_err: - /* This is to catch a BIOS bug workaround where - * mmconfig table doesn't have other buses. - */ - printk(KERN_ERR PFX "%s: can't access PCI config space\n", - pci_name(hw->pdev)); - return err; } static u32 sky2_supported_modes(const struct sky2_hw *hw) @@ -3239,17 +3224,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } } -#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 = kzalloc(sizeof(*hw), GFP_KERNEL); if (!hw) { @@ -3268,6 +3242,17 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } hw->pm_cap = pm_cap; +#ifdef __BIG_ENDIAN + /* byte swap descriptors in hardware */ + { + u32 reg; + + reg = sky2_pci_read32(hw, PCI_DEV_REG2); + reg |= PCI_REV_DESC; + sky2_pci_write32(hw, PCI_DEV_REG2, reg); + } +#endif + /* ring for status responses */ hw->st_le = pci_alloc_consistent(hw->pdev, STATUS_LE_BYTES, &hw->st_dma); diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index d1c71f50d8b9f6..9e40766150b4fb 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -1951,4 +1951,25 @@ static inline void gma_set_addr(struct sky2_hw *hw, unsigned port, unsigned reg, 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)); } + +/* PCI config space access */ +static inline u32 sky2_pci_read32(const struct sky2_hw *hw, unsigned reg) +{ + return sky2_read32(hw, Y2_CFG_SPC + reg); +} + +static inline u16 sky2_pci_read16(const struct sky2_hw *hw, unsigned reg) +{ + return sky2_read16(hw, Y2_CFG_SPC + reg); +} + +static inline void sky2_pci_write32(struct sky2_hw *hw, unsigned reg, u32 val) +{ + sky2_write32(hw, Y2_CFG_SPC + reg, val); +} + +static inline void sky2_pci_write16(struct sky2_hw *hw, unsigned reg, u16 val) +{ + sky2_write16(hw, Y2_CFG_SPC + reg, val); +} #endif -- cgit 1.2.3-korg From 791917deb63c6d8beb3f347ea0911371deff1624 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 11:45:03 -0800 Subject: [PATCH] sky2: close race on IRQ mask update. Need to avoid race in updating IRQ mask. This can probably be replaced smarter use of the interrupt control registers (if/when chipset docs are available). Signed-off-by: Stephen Hemminger --- drivers/net/sky2.c | 21 +++++++++++++++------ drivers/net/sky2.h | 3 ++- 2 files changed, 17 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index fbbc85532c2e41..ca8160d682299a 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1079,8 +1079,10 @@ static int sky2_up(struct net_device *dev) goto err_out; /* Enable interrupts from phy/mac for port */ + spin_lock_irq(&hw->hw_lock); hw->intr_mask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; sky2_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); return 0; err_out: @@ -1380,10 +1382,10 @@ static int sky2_down(struct net_device *dev) netif_stop_queue(dev); /* Disable port IRQ */ - local_irq_disable(); + spin_lock_irq(&hw->hw_lock); 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(); + spin_unlock_irq(&hw->hw_lock); flush_scheduled_work(); @@ -1665,10 +1667,10 @@ static void sky2_phy_task(void *arg) out: up(&sky2->phy_sema); - local_irq_disable(); + spin_lock_irq(&hw->hw_lock); 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(); + spin_unlock_irq(&hw->hw_lock); } @@ -1994,9 +1996,13 @@ exit_loop: } if (likely(work_done < to_do)) { - netif_rx_complete(dev0); + spin_lock_irq(&hw->hw_lock); + __netif_rx_complete(dev0); + hw->intr_mask |= Y2_IS_STAT_BMU; sky2_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); + return 0; } else { *budget -= work_done; @@ -2128,6 +2134,7 @@ static void sky2_phy_intr(struct sky2_hw *hw, unsigned port) hw->intr_mask &= ~(port == 0 ? Y2_IS_IRQ_PHY1 : Y2_IS_IRQ_PHY2); sky2_write32(hw, B0_IMSK, hw->intr_mask); + schedule_work(&sky2->phy_task); } @@ -2141,6 +2148,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) if (status == 0 || status == ~0) return IRQ_NONE; + spin_lock(&hw->hw_lock); if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -2169,7 +2177,7 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) sky2_write32(hw, B0_Y2_SP_ICR, 2); - sky2_read32(hw, B0_IMSK); + spin_unlock(&hw->hw_lock); return IRQ_HANDLED; } @@ -3241,6 +3249,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, goto err_out_free_hw; } hw->pm_cap = pm_cap; + spin_lock_init(&hw->hw_lock); #ifdef __BIG_ENDIAN /* byte swap descriptors in hardware */ diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 9e40766150b4fb..3edb98075e0a94 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -1876,8 +1876,9 @@ struct sky2_port { struct sky2_hw { void __iomem *regs; struct pci_dev *pdev; - u32 intr_mask; struct net_device *dev[2]; + spinlock_t hw_lock; + u32 intr_mask; int pm_cap; int msi; -- cgit 1.2.3-korg From 5d06a99f543e734ceb53bbc9e550537be97f0c49 Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Thu, 23 Feb 2006 00:47:58 +0100 Subject: r8169: fix broken ring index handling in suspend/resume rtl8169_hw_start() requires that the descriptor ring indexes be set to zero. Let a deferred invocation of rtl8169_reset_task() handle it. Enabling a few power management bits will not hurt either. suspend/resume is issued with irq on: the spinlock do not need to save the irq flag. Signed-off-by: Francois Romieu --- drivers/net/r8169.c | 102 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 6e1018448eea9b..999fd6cef77eeb 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -287,6 +287,12 @@ enum RTL8169_register_content { TxInterFrameGapShift = 24, TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + /* Config1 register p.24 */ + PMEnable = (1 << 0), /* Power Management Enable */ + + /* Config5 register p.27 */ + PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ + /* TBICSR p.28 */ TBIReset = 0x80000000, TBILoopback = 0x40000000, @@ -1442,6 +1448,11 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, } tp->chipset = i; + RTL_W8(Cfg9346, Cfg9346_Unlock); + RTL_W8(Config1, RTL_R8(Config1) | PMEnable); + RTL_W8(Config5, RTL_R8(Config5) & PMEStatus); + RTL_W8(Cfg9346, Cfg9346_Lock); + *ioaddr_out = ioaddr; *dev_out = dev; out: @@ -1612,49 +1623,6 @@ rtl8169_remove_one(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } -#ifdef CONFIG_PM - -static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct net_device *dev = pci_get_drvdata(pdev); - struct rtl8169_private *tp = netdev_priv(dev); - void __iomem *ioaddr = tp->mmio_addr; - unsigned long flags; - - if (!netif_running(dev)) - return 0; - - netif_device_detach(dev); - netif_stop_queue(dev); - spin_lock_irqsave(&tp->lock, flags); - - /* Disable interrupts, stop Rx and Tx */ - RTL_W16(IntrMask, 0); - RTL_W8(ChipCmd, 0); - - /* Update the error counts. */ - tp->stats.rx_missed_errors += RTL_R32(RxMissed); - RTL_W32(RxMissed, 0); - spin_unlock_irqrestore(&tp->lock, flags); - - return 0; -} - -static int rtl8169_resume(struct pci_dev *pdev) -{ - struct net_device *dev = pci_get_drvdata(pdev); - - if (!netif_running(dev)) - return 0; - - netif_device_attach(dev); - rtl8169_hw_start(dev); - - return 0; -} - -#endif /* CONFIG_PM */ - static void rtl8169_set_rxbufsize(struct rtl8169_private *tp, struct net_device *dev) { @@ -2700,6 +2668,54 @@ static struct net_device_stats *rtl8169_get_stats(struct net_device *dev) return &tp->stats; } +#ifdef CONFIG_PM + +static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + if (!netif_running(dev)) + goto out; + + netif_device_detach(dev); + netif_stop_queue(dev); + + spin_lock_irq(&tp->lock); + + rtl8169_asic_down(ioaddr); + + tp->stats.rx_missed_errors += RTL_R32(RxMissed); + RTL_W32(RxMissed, 0); + + spin_unlock_irq(&tp->lock); + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); +out: + return 0; +} + +static int rtl8169_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (!netif_running(dev)) + goto out; + + netif_device_attach(dev); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + rtl8169_schedule_work(dev, rtl8169_reset_task); +out: + return 0; +} + +#endif /* CONFIG_PM */ + static struct pci_driver rtl8169_pci_driver = { .name = MODULENAME, .id_table = rtl8169_pci_tbl, -- cgit 1.2.3-korg From 61a4dcc2f9b5c6861e7198b80dd73dd6e9247b7b Mon Sep 17 00:00:00 2001 From: Francois Romieu Date: Thu, 23 Feb 2006 00:55:25 +0100 Subject: r8169: enable wake on lan Similar to 8139cp code but more inspired/lucky. Signed-off-by: Francois Romieu --- drivers/net/r8169.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'drivers') diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 999fd6cef77eeb..8cc0d0bbdf50f1 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -290,7 +290,15 @@ enum RTL8169_register_content { /* Config1 register p.24 */ PMEnable = (1 << 0), /* Power Management Enable */ + /* Config3 register p.25 */ + MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ + LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ + /* Config5 register p.27 */ + BWF = (1 << 6), /* Accept Broadcast wakeup frame */ + MWF = (1 << 5), /* Accept Multicast wakeup frame */ + UWF = (1 << 4), /* Accept Unicast wakeup frame */ + LanWake = (1 << 1), /* LanWake enable/disable */ PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ /* TBICSR p.28 */ @@ -439,6 +447,7 @@ struct rtl8169_private { unsigned int (*phy_reset_pending)(void __iomem *); unsigned int (*link_ok)(void __iomem *); struct work_struct task; + unsigned wol_enabled : 1; }; MODULE_AUTHOR("Realtek and the Linux r8169 crew "); @@ -613,6 +622,80 @@ static void rtl8169_link_option(int idx, u8 *autoneg, u16 *speed, u8 *duplex) *duplex = p->duplex; } +static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u8 options; + + wol->wolopts = 0; + +#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) + wol->supported = WAKE_ANY; + + spin_lock_irq(&tp->lock); + + options = RTL_R8(Config1); + if (!(options & PMEnable)) + goto out_unlock; + + options = RTL_R8(Config3); + if (options & LinkUp) + wol->wolopts |= WAKE_PHY; + if (options & MagicPacket) + wol->wolopts |= WAKE_MAGIC; + + options = RTL_R8(Config5); + if (options & UWF) + wol->wolopts |= WAKE_UCAST; + if (options & BWF) + wol->wolopts |= WAKE_BCAST; + if (options & MWF) + wol->wolopts |= WAKE_MCAST; + +out_unlock: + spin_unlock_irq(&tp->lock); +} + +static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + int i; + static struct { + u32 opt; + u16 reg; + u8 mask; + } cfg[] = { + { WAKE_ANY, Config1, PMEnable }, + { WAKE_PHY, Config3, LinkUp }, + { WAKE_MAGIC, Config3, MagicPacket }, + { WAKE_UCAST, Config5, UWF }, + { WAKE_BCAST, Config5, BWF }, + { WAKE_MCAST, Config5, MWF }, + { WAKE_ANY, Config5, LanWake } + }; + + spin_lock_irq(&tp->lock); + + RTL_W8(Cfg9346, Cfg9346_Unlock); + + for (i = 0; i < ARRAY_SIZE(cfg); i++) { + u8 options = RTL_R8(cfg[i].reg) & ~cfg[i].mask; + if (wol->wolopts & cfg[i].opt) + options |= cfg[i].mask; + RTL_W8(cfg[i].reg, options); + } + + RTL_W8(Cfg9346, Cfg9346_Lock); + + tp->wol_enabled = (wol->wolopts) ? 1 : 0; + + spin_unlock_irq(&tp->lock); + + return 0; +} + static void rtl8169_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { @@ -1031,6 +1114,8 @@ static struct ethtool_ops rtl8169_ethtool_ops = { .get_tso = ethtool_op_get_tso, .set_tso = ethtool_op_set_tso, .get_regs = rtl8169_get_regs, + .get_wol = rtl8169_get_wol, + .set_wol = rtl8169_set_wol, .get_strings = rtl8169_get_strings, .get_stats_count = rtl8169_get_stats_count, .get_ethtool_stats = rtl8169_get_ethtool_stats, @@ -2692,6 +2777,7 @@ static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state) spin_unlock_irq(&tp->lock); pci_save_state(pdev); + pci_enable_wake(pdev, pci_choose_state(pdev, state), tp->wol_enabled); pci_set_power_state(pdev, pci_choose_state(pdev, state)); out: return 0; @@ -2708,6 +2794,7 @@ static int rtl8169_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); rtl8169_schedule_work(dev, rtl8169_reset_task); out: -- cgit 1.2.3-korg From a9cdab869ec343ccc601484fb535813e16c25f70 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 10:28:33 -0800 Subject: skge: NAPI/irq race fix Fix a race in the receive NAPI, irq handling. The interrupt clear and the start need to be separated. Otherwise there is a window between the last frame received and the NAPI done level handling. Signed-off-by: Stephen Hemminger --- drivers/net/skge.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 67fb19b8fde929..869c7cfb99a4e5 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -2678,8 +2678,7 @@ static int skge_poll(struct net_device *dev, int *budget) /* restart receiver */ wmb(); - skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), - CSR_START | CSR_IRQ_CL_F); + skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), CSR_START); *budget -= work_done; dev->quota -= work_done; @@ -2856,14 +2855,6 @@ static void skge_extirq(unsigned long data) 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; @@ -2874,13 +2865,15 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) status &= hw->intr_mask; if (status & IS_R1_F) { + skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F); hw->intr_mask &= ~IS_R1_F; - skge_wakeup(hw->dev[0]); + netif_rx_schedule(hw->dev[0]); } if (status & IS_R2_F) { + skge_write8(hw, Q_ADDR(Q_R2, Q_CSR), CSR_IRQ_CL_F); hw->intr_mask &= ~IS_R2_F; - skge_wakeup(hw->dev[1]); + netif_rx_schedule(hw->dev[1]); } if (status & IS_XA1_F) -- cgit 1.2.3-korg From 0781191cf69b7635e0d3ea55c6019e789d1936fa Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 10:28:34 -0800 Subject: skge: genesis phy initialzation The SysKonnect Genesis based board would fail on initialization with phy_read errors caused by not waiting for last phy write. Signed-off-by: Stephen Hemminger --- drivers/net/skge.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 869c7cfb99a4e5..af2e6782031b54 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -879,13 +879,12 @@ static int __xm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val) int i; xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr); - xm_read16(hw, port, XM_PHY_DATA); + *val = xm_read16(hw, port, XM_PHY_DATA); - /* Need to wait for external PHY */ for (i = 0; i < PHY_RETRIES; i++) { - udelay(1); if (xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_RDY) goto ready; + udelay(1); } return -ETIMEDOUT; @@ -918,7 +917,12 @@ static int xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val) ready: xm_write16(hw, port, XM_PHY_DATA, val); - return 0; + for (i = 0; i < PHY_RETRIES; i++) { + if (!(xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_BUSY)) + return 0; + udelay(1); + } + return -ETIMEDOUT; } static void genesis_init(struct skge_hw *hw) @@ -1168,13 +1172,17 @@ static void genesis_mac_init(struct skge_hw *hw, int port) u32 r; const u8 zero[6] = { 0 }; - /* Clear MIB counters */ - xm_write16(hw, port, XM_STAT_CMD, - XM_SC_CLR_RXC | XM_SC_CLR_TXC); - /* Clear two times according to Errata #3 */ - xm_write16(hw, port, XM_STAT_CMD, - XM_SC_CLR_RXC | XM_SC_CLR_TXC); + for (i = 0; i < 10; i++) { + skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), + MFF_SET_MAC_RST); + if (skge_read16(hw, SK_REG(port, TX_MFF_CTRL1)) & MFF_SET_MAC_RST) + goto reset_ok; + udelay(1); + } + printk(KERN_WARNING PFX "%s: genesis reset failed\n", dev->name); + + reset_ok: /* Unreset the XMAC. */ skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_CLR_MAC_RST); @@ -1191,7 +1199,7 @@ static void genesis_mac_init(struct skge_hw *hw, int port) r |= GP_DIR_2|GP_IO_2; skge_write32(hw, B2_GP_IO, r); - skge_read32(hw, B2_GP_IO); + /* Enable GMII interface */ xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD); @@ -1205,6 +1213,13 @@ static void genesis_mac_init(struct skge_hw *hw, int port) for (i = 1; i < 16; i++) xm_outaddr(hw, port, XM_EXM(i), zero); + /* Clear MIB counters */ + xm_write16(hw, port, XM_STAT_CMD, + XM_SC_CLR_RXC | XM_SC_CLR_TXC); + /* Clear two times according to Errata #3 */ + xm_write16(hw, port, XM_STAT_CMD, + XM_SC_CLR_RXC | XM_SC_CLR_TXC); + /* configure Rx High Water Mark (XM_RX_HI_WM) */ xm_write16(hw, port, XM_RX_HI_WM, 1450); -- cgit 1.2.3-korg From 80dd857daca1cf541b10118991569470d62c1d38 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 22 Feb 2006 10:28:35 -0800 Subject: skge: protect interrupt mask There is a race between updating the irq mask and setting it which can be triggered on SMP with a bad cable. Similar patch from Ingo Molnar and Thomas Gleixner Signed-off-by: Stephen Hemminger --- drivers/net/skge.c | 21 ++++++++++++++------- drivers/net/skge.h | 1 + 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/skge.c b/drivers/net/skge.c index af2e6782031b54..25e028b7ce4805 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -2185,8 +2185,10 @@ static int skge_up(struct net_device *dev) skge->tx_avail = skge->tx_ring.count - 1; /* Enable IRQ from port */ + spin_lock_irq(&hw->hw_lock); hw->intr_mask |= portirqmask[port]; skge_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); /* Initialize MAC */ spin_lock_bh(&hw->phy_lock); @@ -2244,8 +2246,10 @@ static int skge_down(struct net_device *dev) else yukon_stop(skge); + spin_lock_irq(&hw->hw_lock); hw->intr_mask &= ~portirqmask[skge->port]; skge_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); /* Stop transmitter */ skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP); @@ -2701,10 +2705,11 @@ static int skge_poll(struct net_device *dev, int *budget) if (work_done >= to_do) return 1; /* not done */ - netif_rx_complete(dev); - hw->intr_mask |= portirqmask[skge->port]; - skge_write32(hw, B0_IMSK, hw->intr_mask); - skge_read32(hw, B0_IMSK); + spin_lock_irq(&hw->hw_lock); + __netif_rx_complete(dev); + hw->intr_mask |= portirqmask[skge->port]; + skge_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); return 0; } @@ -2864,10 +2869,10 @@ static void skge_extirq(unsigned long data) } spin_unlock(&hw->phy_lock); - local_irq_disable(); + spin_lock_irq(&hw->hw_lock); hw->intr_mask |= IS_EXT_REG; skge_write32(hw, B0_IMSK, hw->intr_mask); - local_irq_enable(); + spin_unlock_irq(&hw->hw_lock); } static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) @@ -2878,7 +2883,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) if (status == 0 || status == ~0) /* hotplug or shared irq */ return IRQ_NONE; - status &= hw->intr_mask; + spin_lock(&hw->hw_lock); if (status & IS_R1_F) { skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F); hw->intr_mask &= ~IS_R1_F; @@ -2930,6 +2935,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) } skge_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock(&hw->hw_lock); return IRQ_HANDLED; } @@ -3298,6 +3304,7 @@ static int __devinit skge_probe(struct pci_dev *pdev, hw->pdev = pdev; spin_lock_init(&hw->phy_lock); + spin_lock_init(&hw->hw_lock); tasklet_init(&hw->ext_tasklet, skge_extirq, (unsigned long) hw); hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); diff --git a/drivers/net/skge.h b/drivers/net/skge.h index 2efdacc290e589..941f12a333b639 100644 --- a/drivers/net/skge.h +++ b/drivers/net/skge.h @@ -2402,6 +2402,7 @@ struct skge_hw { struct tasklet_struct ext_tasklet; spinlock_t phy_lock; + spinlock_t hw_lock; }; enum { -- cgit 1.2.3-korg From ad9f6713ae59f319ed676c2d014a7756b62f1c51 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 5 Feb 2006 00:37:47 +0100 Subject: [PATCH] drivers/net/tlan.c: #ifdef CONFIG_PCI the PCI specific code drivers/net/tlan.c compiles with CONFIG_PCI=n only with a warning and due to the dead code elimination of gcc. Additionally, this fixes the only compile error I found with CONFIG_PCI=n and the gcc -Werror-implicit-function-declaration flag on i386. Signed-off-by: Adrian Bunk Signed-off-by: Jeff Garzik --- drivers/net/tlan.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index c2506b56a18691..12076f8f942c7d 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -536,6 +536,7 @@ static int __devinit TLan_probe1(struct pci_dev *pdev, u16 device_id; int reg, rc = -ENODEV; +#ifdef CONFIG_PCI if (pdev) { rc = pci_enable_device(pdev); if (rc) @@ -547,6 +548,7 @@ static int __devinit TLan_probe1(struct pci_dev *pdev, goto err_out; } } +#endif /* CONFIG_PCI */ dev = alloc_etherdev(sizeof(TLanPrivateInfo)); if (dev == NULL) { -- cgit 1.2.3-korg