diff options
Diffstat (limited to 'drivers/net')
41 files changed, 2791 insertions, 422 deletions
diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c index 62d6f88cbab..aa07657744c 100644 --- a/drivers/net/arm/ks8695net.c +++ b/drivers/net/arm/ks8695net.c @@ -1644,7 +1644,7 @@ ks8695_cleanup(void) module_init(ks8695_init); module_exit(ks8695_cleanup); -MODULE_AUTHOR("Simtec Electronics") +MODULE_AUTHOR("Simtec Electronics"); MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" MODULENAME); diff --git a/drivers/net/atl1c/atl1c_hw.c b/drivers/net/atl1c/atl1c_hw.c index 1bf67200994..23f2ab0f2fa 100644 --- a/drivers/net/atl1c/atl1c_hw.c +++ b/drivers/net/atl1c/atl1c_hw.c @@ -345,7 +345,7 @@ int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data) */ static int atl1c_phy_setup_adv(struct atl1c_hw *hw) { - u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_SPEED_MASK; + u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL; u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP & ~GIGA_CR_1000T_SPEED_MASK; @@ -373,7 +373,7 @@ static int atl1c_phy_setup_adv(struct atl1c_hw *hw) } if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 || - atl1c_write_phy_reg(hw, MII_GIGA_CR, mii_giga_ctrl_data) != 0) + atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0) return -1; return 0; } @@ -517,19 +517,18 @@ int atl1c_phy_init(struct atl1c_hw *hw) "Error Setting up Auto-Negotiation\n"); return ret_val; } - mii_bmcr_data |= BMCR_AUTO_NEG_EN | BMCR_RESTART_AUTO_NEG; + mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART; break; case MEDIA_TYPE_100M_FULL: - mii_bmcr_data |= BMCR_SPEED_100 | BMCR_FULL_DUPLEX; + mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX; break; case MEDIA_TYPE_100M_HALF: - mii_bmcr_data |= BMCR_SPEED_100; + mii_bmcr_data |= BMCR_SPEED100; break; case MEDIA_TYPE_10M_FULL: - mii_bmcr_data |= BMCR_SPEED_10 | BMCR_FULL_DUPLEX; + mii_bmcr_data |= BMCR_FULLDPLX; break; case MEDIA_TYPE_10M_HALF: - mii_bmcr_data |= BMCR_SPEED_10; break; default: if (netif_msg_link(adapter)) @@ -657,7 +656,7 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw) err = atl1c_phy_setup_adv(hw); if (err) return err; - mii_bmcr_data |= BMCR_AUTO_NEG_EN | BMCR_RESTART_AUTO_NEG; + mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART; return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); } diff --git a/drivers/net/atl1c/atl1c_hw.h b/drivers/net/atl1c/atl1c_hw.h index 3dd675979aa..655fc6c4a8a 100644 --- a/drivers/net/atl1c/atl1c_hw.h +++ b/drivers/net/atl1c/atl1c_hw.h @@ -736,55 +736,16 @@ int atl1c_phy_power_saving(struct atl1c_hw *hw); #define REG_DEBUG_DATA0 0x1900 #define REG_DEBUG_DATA1 0x1904 -/* PHY Control Register */ -#define MII_BMCR 0x00 -#define BMCR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define BMCR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ -#define BMCR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ -#define BMCR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ -#define BMCR_ISOLATE 0x0400 /* Isolate PHY from MII */ -#define BMCR_POWER_DOWN 0x0800 /* Power down */ -#define BMCR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ -#define BMCR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define BMCR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ -#define BMCR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ -#define BMCR_SPEED_MASK 0x2040 -#define BMCR_SPEED_1000 0x0040 -#define BMCR_SPEED_100 0x2000 -#define BMCR_SPEED_10 0x0000 - -/* PHY Status Register */ -#define MII_BMSR 0x01 -#define BMMSR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define BMSR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define BMSR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define BMSR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define BMSR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define BMSR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define BMSR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define BMSR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define BMSR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define BMSR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define BMSR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define BMSR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define BMSR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define BMMII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define BMMII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ - -#define MII_PHYSID1 0x02 -#define MII_PHYSID2 0x03 #define L1D_MPW_PHYID1 0xD01C /* V7 */ #define L1D_MPW_PHYID2 0xD01D /* V1-V6 */ #define L1D_MPW_PHYID3 0xD01E /* V8 */ /* Autoneg Advertisement Register */ -#define MII_ADVERTISE 0x04 -#define ADVERTISE_SPEED_MASK 0x01E0 -#define ADVERTISE_DEFAULT_CAP 0x0DE0 +#define ADVERTISE_DEFAULT_CAP \ + (ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM) /* 1000BASE-T Control Register */ -#define MII_GIGA_CR 0x09 #define GIGA_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port 0=DTE device */ #define GIGA_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master 0=Configure PHY as Slave */ diff --git a/drivers/net/atl1e/atl1e_ethtool.c b/drivers/net/atl1e/atl1e_ethtool.c index 6943a6c3b94..1209297433b 100644 --- a/drivers/net/atl1e/atl1e_ethtool.c +++ b/drivers/net/atl1e/atl1e_ethtool.c @@ -95,18 +95,18 @@ static int atl1e_set_settings(struct net_device *netdev, ecmd->advertising = hw->autoneg_advertised | ADVERTISED_TP | ADVERTISED_Autoneg; - adv4 = hw->mii_autoneg_adv_reg & ~MII_AR_SPEED_MASK; + adv4 = hw->mii_autoneg_adv_reg & ~ADVERTISE_ALL; adv9 = hw->mii_1000t_ctrl_reg & ~MII_AT001_CR_1000T_SPEED_MASK; if (hw->autoneg_advertised & ADVERTISE_10_HALF) - adv4 |= MII_AR_10T_HD_CAPS; + adv4 |= ADVERTISE_10HALF; if (hw->autoneg_advertised & ADVERTISE_10_FULL) - adv4 |= MII_AR_10T_FD_CAPS; + adv4 |= ADVERTISE_10FULL; if (hw->autoneg_advertised & ADVERTISE_100_HALF) - adv4 |= MII_AR_100TX_HD_CAPS; + adv4 |= ADVERTISE_100HALF; if (hw->autoneg_advertised & ADVERTISE_100_FULL) - adv4 |= MII_AR_100TX_FD_CAPS; + adv4 |= ADVERTISE_100FULL; if (hw->autoneg_advertised & ADVERTISE_1000_FULL) - adv9 |= MII_AT001_CR_1000T_FD_CAPS; + adv9 |= ADVERTISE_1000FULL; if (adv4 != hw->mii_autoneg_adv_reg || adv9 != hw->mii_1000t_ctrl_reg) { diff --git a/drivers/net/atl1e/atl1e_hw.c b/drivers/net/atl1e/atl1e_hw.c index 76cc043def8..923063d2e5b 100644 --- a/drivers/net/atl1e/atl1e_hw.c +++ b/drivers/net/atl1e/atl1e_hw.c @@ -318,7 +318,7 @@ static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw) * 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_autoneg_adv_reg &= ~ADVERTISE_ALL; mii_1000t_ctrl_reg &= ~MII_AT001_CR_1000T_SPEED_MASK; /* @@ -327,44 +327,37 @@ static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw) */ 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); - hw->autoneg_advertised = ADVERTISE_10_HALF | - ADVERTISE_10_FULL | - ADVERTISE_100_HALF | - ADVERTISE_100_FULL; + mii_autoneg_adv_reg |= ADVERTISE_ALL; + hw->autoneg_advertised = ADVERTISE_ALL; if (hw->nic_type == athr_l1e) { - mii_1000t_ctrl_reg |= - MII_AT001_CR_1000T_FD_CAPS; + mii_1000t_ctrl_reg |= ADVERTISE_1000FULL; hw->autoneg_advertised |= ADVERTISE_1000_FULL; } break; case MEDIA_TYPE_100M_FULL: - mii_autoneg_adv_reg |= MII_AR_100TX_FD_CAPS; + mii_autoneg_adv_reg |= ADVERTISE_100FULL; hw->autoneg_advertised = ADVERTISE_100_FULL; break; case MEDIA_TYPE_100M_HALF: - mii_autoneg_adv_reg |= MII_AR_100TX_HD_CAPS; + mii_autoneg_adv_reg |= ADVERTISE_100_HALF; hw->autoneg_advertised = ADVERTISE_100_HALF; break; case MEDIA_TYPE_10M_FULL: - mii_autoneg_adv_reg |= MII_AR_10T_FD_CAPS; + mii_autoneg_adv_reg |= ADVERTISE_10_FULL; hw->autoneg_advertised = ADVERTISE_10_FULL; break; default: - mii_autoneg_adv_reg |= MII_AR_10T_HD_CAPS; + mii_autoneg_adv_reg |= ADVERTISE_10_HALF; hw->autoneg_advertised = ADVERTISE_10_HALF; break; } /* flow control fixed to enable all */ - mii_autoneg_adv_reg |= (MII_AR_ASM_DIR | MII_AR_PAUSE); + mii_autoneg_adv_reg |= (ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP); hw->mii_autoneg_adv_reg = mii_autoneg_adv_reg; hw->mii_1000t_ctrl_reg = mii_1000t_ctrl_reg; @@ -374,7 +367,7 @@ static int atl1e_phy_setup_autoneg_adv(struct atl1e_hw *hw) return ret_val; if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) { - ret_val = atl1e_write_phy_reg(hw, MII_AT001_CR, + ret_val = atl1e_write_phy_reg(hw, MII_CTRL1000, mii_1000t_ctrl_reg); if (ret_val) return ret_val; @@ -397,7 +390,7 @@ int atl1e_phy_commit(struct atl1e_hw *hw) int ret_val; u16 phy_data; - phy_data = MII_CR_RESET | MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG; + phy_data = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART; ret_val = atl1e_write_phy_reg(hw, MII_BMCR, phy_data); if (ret_val) { @@ -645,15 +638,14 @@ int atl1e_restart_autoneg(struct atl1e_hw *hw) return err; if (hw->nic_type == athr_l1e || hw->nic_type == athr_l2e_revA) { - err = atl1e_write_phy_reg(hw, MII_AT001_CR, + err = atl1e_write_phy_reg(hw, MII_CTRL1000, hw->mii_1000t_ctrl_reg); if (err) return err; } err = atl1e_write_phy_reg(hw, MII_BMCR, - MII_CR_RESET | MII_CR_AUTO_NEG_EN | - MII_CR_RESTART_AUTO_NEG); + BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART); return err; } diff --git a/drivers/net/atl1e/atl1e_hw.h b/drivers/net/atl1e/atl1e_hw.h index 5ea2f4d86cf..74df16aef79 100644 --- a/drivers/net/atl1e/atl1e_hw.h +++ b/drivers/net/atl1e/atl1e_hw.h @@ -629,127 +629,24 @@ s32 atl1e_restart_autoneg(struct atl1e_hw *hw); /***************************** MII definition ***************************************/ /* PHY Common Register */ -#define MII_BMCR 0x00 -#define MII_BMSR 0x01 -#define MII_PHYSID1 0x02 -#define MII_PHYSID2 0x03 -#define MII_ADVERTISE 0x04 -#define MII_LPA 0x05 -#define MII_EXPANSION 0x06 -#define MII_AT001_CR 0x09 -#define MII_AT001_SR 0x0A -#define MII_AT001_ESR 0x0F #define MII_AT001_PSCR 0x10 #define MII_AT001_PSSR 0x11 #define MII_INT_CTRL 0x12 #define MII_INT_STATUS 0x13 #define MII_SMARTSPEED 0x14 -#define MII_RERRCOUNTER 0x15 -#define MII_SREVISION 0x16 -#define MII_RESV1 0x17 #define MII_LBRERROR 0x18 -#define MII_PHYADDR 0x19 #define MII_RESV2 0x1a -#define MII_TPISTATUS 0x1b -#define MII_NCONFIG 0x1c #define MII_DBG_ADDR 0x1D #define MII_DBG_DATA 0x1E - -/* PHY Control Register */ -#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ -#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ -#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ -#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ -#define MII_CR_POWER_DOWN 0x0800 /* Power down */ -#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ -#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ -#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ -#define MII_CR_SPEED_MASK 0x2040 -#define MII_CR_SPEED_1000 0x0040 -#define MII_CR_SPEED_100 0x2000 -#define MII_CR_SPEED_10 0x0000 - - -/* PHY Status Register */ -#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ - -/* Link partner ability register. */ -#define MII_LPA_SLCT 0x001f /* Same as advertise selector */ -#define MII_LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ -#define MII_LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ -#define MII_LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ -#define MII_LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ -#define MII_LPA_100BASE4 0x0200 /* 100BASE-T4 */ -#define MII_LPA_PAUSE 0x0400 /* PAUSE */ -#define MII_LPA_ASYPAUSE 0x0800 /* Asymmetrical PAUSE */ -#define MII_LPA_RFAULT 0x2000 /* Link partner faulted */ -#define MII_LPA_LPACK 0x4000 /* Link partner acked us */ -#define MII_LPA_NPAGE 0x8000 /* Next page bit */ - /* Autoneg Advertisement Register */ -#define MII_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ -#define MII_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ -#define MII_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ -#define MII_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ -#define MII_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ -#define MII_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ -#define MII_AR_PAUSE 0x0400 /* Pause operation desired */ -#define MII_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ -#define MII_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ -#define MII_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ -#define MII_AR_SPEED_MASK 0x01E0 -#define MII_AR_DEFAULT_CAP_MASK 0x0DE0 +#define MII_AR_DEFAULT_CAP_MASK 0 /* 1000BASE-T Control Register */ -#define MII_AT001_CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ -#define MII_AT001_CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ -#define MII_AT001_CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ -/* 0=DTE device */ -#define MII_AT001_CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ -/* 0=Configure PHY as Slave */ -#define MII_AT001_CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ -/* 0=Automatic Master/Slave config */ -#define MII_AT001_CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ -#define MII_AT001_CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ -#define MII_AT001_CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ -#define MII_AT001_CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ -#define MII_AT001_CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ -#define MII_AT001_CR_1000T_SPEED_MASK 0x0300 -#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK 0x0300 - -/* 1000BASE-T Status Register */ -#define MII_AT001_SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ -#define MII_AT001_SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ -#define MII_AT001_SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ -#define MII_AT001_SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ -#define MII_AT001_SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ -#define MII_AT001_SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ -#define MII_AT001_SR_1000T_REMOTE_RX_STATUS_SHIFT 12 -#define MII_AT001_SR_1000T_LOCAL_RX_STATUS_SHIFT 13 - -/* Extended Status Register */ -#define MII_AT001_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ -#define MII_AT001_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ -#define MII_AT001_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ -#define MII_AT001_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ +#define MII_AT001_CR_1000T_SPEED_MASK \ + (ADVERTISE_1000FULL | ADVERTISE_1000HALF) +#define MII_AT001_CR_1000T_DEFAULT_CAP_MASK MII_AT001_CR_1000T_SPEED_MASK /* AT001 PHY Specific Control Register */ #define MII_AT001_PSCR_JABBER_DISABLE 0x0001 /* 1=Jabber Function disabled */ diff --git a/drivers/net/atl1e/atl1e_main.c b/drivers/net/atl1e/atl1e_main.c index e28f8baf394..bf7500ccd73 100644 --- a/drivers/net/atl1e/atl1e_main.c +++ b/drivers/net/atl1e/atl1e_main.c @@ -2051,9 +2051,9 @@ static int atl1e_suspend(struct pci_dev *pdev, pm_message_t state) atl1e_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); atl1e_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); - mii_advertise_data = MII_AR_10T_HD_CAPS; + mii_advertise_data = ADVERTISE_10HALF; - if ((atl1e_write_phy_reg(hw, MII_AT001_CR, 0) != 0) || + if ((atl1e_write_phy_reg(hw, MII_CTRL1000, 0) != 0) || (atl1e_write_phy_reg(hw, MII_ADVERTISE, mii_advertise_data) != 0) || (atl1e_phy_commit(hw)) != 0) { diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 171782e2bb3..1024ae15822 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2470,6 +2470,10 @@ int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct pac if (!(dev->flags & IFF_MASTER)) goto out; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out; + if (!pskb_may_pull(skb, sizeof(struct lacpdu))) goto out; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index f4e638c6512..5c6fba802f2 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -326,6 +326,10 @@ static int rlb_arp_recv(struct sk_buff *skb, struct net_device *bond_dev, struct goto out; } + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out; + if (!pskb_may_pull(skb, arp_hdr_len(bond_dev))) goto out; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b1025b85acf..163e0b06eaa 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2733,6 +2733,10 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack if (!slave || !slave_do_arp_validate(bond, slave)) goto out_unlock; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out_unlock; + if (!pskb_may_pull(skb, arp_hdr_len(dev))) goto out_unlock; diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d5a9db60ade..986195eaa57 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -117,6 +117,8 @@ source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/usb/Kconfig" +source "drivers/net/can/softing/Kconfig" + config CAN_DEBUG_DEVICES bool "CAN devices debugging messages" depends on CAN diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 07ca159ba3f..53c82a71778 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CAN_DEV) += can-dev.o can-dev-y := dev.o obj-y += usb/ +obj-y += softing/ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_MSCAN) += mscan/ diff --git a/drivers/net/can/softing/Kconfig b/drivers/net/can/softing/Kconfig new file mode 100644 index 00000000000..92bd6bdde5e --- /dev/null +++ b/drivers/net/can/softing/Kconfig @@ -0,0 +1,30 @@ +config CAN_SOFTING + tristate "Softing Gmbh CAN generic support" + depends on CAN_DEV + ---help--- + Support for CAN cards from Softing Gmbh & some cards + from Vector Gmbh. + Softing Gmbh CAN cards come with 1 or 2 physical busses. + Those cards typically use Dual Port RAM to communicate + with the host CPU. The interface is then identical for PCI + and PCMCIA cards. This driver operates on a platform device, + which has been created by softing_cs or softing_pci driver. + Warning: + The API of the card does not allow fine control per bus, but + controls the 2 busses on the card together. + As such, some actions (start/stop/busoff recovery) on 1 bus + must bring down the other bus too temporarily. + +config CAN_SOFTING_CS + tristate "Softing Gmbh CAN pcmcia cards" + depends on PCMCIA + select CAN_SOFTING + ---help--- + Support for PCMCIA cards from Softing Gmbh & some cards + from Vector Gmbh. + You need firmware for these, which you can get at + http://developer.berlios.de/projects/socketcan/ + This version of the driver is written against + firmware version 4.6 (softing-fw-4.6-binaries.tar.gz) + In order to use the card as CAN device, you need the Softing generic + support too. diff --git a/drivers/net/can/softing/Makefile b/drivers/net/can/softing/Makefile new file mode 100644 index 00000000000..c5e5016c742 --- /dev/null +++ b/drivers/net/can/softing/Makefile @@ -0,0 +1,6 @@ + +softing-y := softing_main.o softing_fw.o +obj-$(CONFIG_CAN_SOFTING) += softing.o +obj-$(CONFIG_CAN_SOFTING_CS) += softing_cs.o + +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h new file mode 100644 index 00000000000..7ec9f4db3d5 --- /dev/null +++ b/drivers/net/can/softing/softing.h @@ -0,0 +1,167 @@ +/* + * softing common interfaces + * + * by Kurt Van Dijck, 2008-2010 + */ + +#include <linux/atomic.h> +#include <linux/netdevice.h> +#include <linux/ktime.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/can.h> +#include <linux/can/dev.h> + +#include "softing_platform.h" + +struct softing; + +struct softing_priv { + struct can_priv can; /* must be the first member! */ + struct net_device *netdev; + struct softing *card; + struct { + int pending; + /* variables wich hold the circular buffer */ + int echo_put; + int echo_get; + } tx; + struct can_bittiming_const btr_const; + int index; + uint8_t output; + uint16_t chip; +}; +#define netdev2softing(netdev) ((struct softing_priv *)netdev_priv(netdev)) + +struct softing { + const struct softing_platform_data *pdat; + struct platform_device *pdev; + struct net_device *net[2]; + spinlock_t spin; /* protect this structure & DPRAM access */ + ktime_t ts_ref; + ktime_t ts_overflow; /* timestamp overflow value, in ktime */ + + struct { + /* indication of firmware status */ + int up; + /* protection of the 'up' variable */ + struct mutex lock; + } fw; + struct { + int nr; + int requested; + int svc_count; + unsigned int dpram_position; + } irq; + struct { + int pending; + int last_bus; + /* + * keep the bus that last tx'd a message, + * in order to let every netdev queue resume + */ + } tx; + __iomem uint8_t *dpram; + unsigned long dpram_phys; + unsigned long dpram_size; + struct { + uint16_t fw_version, hw_version, license, serial; + uint16_t chip[2]; + unsigned int freq; /* remote cpu's operating frequency */ + } id; +}; + +extern int softing_default_output(struct net_device *netdev); + +extern ktime_t softing_raw2ktime(struct softing *card, u32 raw); + +extern int softing_chip_poweron(struct softing *card); + +extern int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg); + +/* Load firmware after reset */ +extern int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *virt, unsigned int size, int offset); + +/* Load final application firmware after bootloader */ +extern int softing_load_app_fw(const char *file, struct softing *card); + +/* + * enable or disable irq + * only called with fw.lock locked + */ +extern int softing_enable_irq(struct softing *card, int enable); + +/* start/stop 1 bus on card */ +extern int softing_startstop(struct net_device *netdev, int up); + +/* netif_rx() */ +extern int softing_netdev_rx(struct net_device *netdev, + const struct can_frame *msg, ktime_t ktime); + +/* SOFTING DPRAM mappings */ +#define DPRAM_RX 0x0000 + #define DPRAM_RX_SIZE 32 + #define DPRAM_RX_CNT 16 +#define DPRAM_RX_RD 0x0201 /* uint8_t */ +#define DPRAM_RX_WR 0x0205 /* uint8_t */ +#define DPRAM_RX_LOST 0x0207 /* uint8_t */ + +#define DPRAM_FCT_PARAM 0x0300 /* int16_t [20] */ +#define DPRAM_FCT_RESULT 0x0328 /* int16_t */ +#define DPRAM_FCT_HOST 0x032b /* uint16_t */ + +#define DPRAM_INFO_BUSSTATE 0x0331 /* uint16_t */ +#define DPRAM_INFO_BUSSTATE2 0x0335 /* uint16_t */ +#define DPRAM_INFO_ERRSTATE 0x0339 /* uint16_t */ +#define DPRAM_INFO_ERRSTATE2 0x033d /* uint16_t */ +#define DPRAM_RESET 0x0341 /* uint16_t */ +#define DPRAM_CLR_RECV_FIFO 0x0345 /* uint16_t */ +#define DPRAM_RESET_TIME 0x034d /* uint16_t */ +#define DPRAM_TIME 0x0350 /* uint64_t */ +#define DPRAM_WR_START 0x0358 /* uint8_t */ +#define DPRAM_WR_END 0x0359 /* uint8_t */ +#define DPRAM_RESET_RX_FIFO 0x0361 /* uint16_t */ +#define DPRAM_RESET_TX_FIFO 0x0364 /* uint8_t */ +#define DPRAM_READ_FIFO_LEVEL 0x0365 /* uint8_t */ +#define DPRAM_RX_FIFO_LEVEL 0x0366 /* uint16_t */ +#define DPRAM_TX_FIFO_LEVEL 0x0366 /* uint16_t */ + +#define DPRAM_TX 0x0400 /* uint16_t */ + #define DPRAM_TX_SIZE 16 + #define DPRAM_TX_CNT 32 +#define DPRAM_TX_RD 0x0601 /* uint8_t */ +#define DPRAM_TX_WR 0x0605 /* uint8_t */ + +#define DPRAM_COMMAND 0x07e0 /* uint16_t */ +#define DPRAM_RECEIPT 0x07f0 /* uint16_t */ +#define DPRAM_IRQ_TOHOST 0x07fe /* uint8_t */ +#define DPRAM_IRQ_TOCARD 0x07ff /* uint8_t */ + +#define DPRAM_V2_RESET 0x0e00 /* uint8_t */ +#define DPRAM_V2_IRQ_TOHOST 0x0e02 /* uint8_t */ + +#define TXMAX (DPRAM_TX_CNT - 1) + +/* DPRAM return codes */ +#define RES_NONE 0 +#define RES_OK 1 +#define RES_NOK 2 +#define RES_UNKNOWN 3 +/* DPRAM flags */ +#define CMD_TX 0x01 +#define CMD_ACK 0x02 +#define CMD_XTD 0x04 +#define CMD_RTR 0x08 +#define CMD_ERR 0x10 +#define CMD_BUS2 0x80 + +/* returned fifo entry bus state masks */ +#define SF_MASK_BUSOFF 0x80 +#define SF_MASK_EPASSIVE 0x60 + +/* bus states */ +#define STATE_BUSOFF 2 +#define STATE_EPASSIVE 1 +#define STATE_EACTIVE 0 diff --git a/drivers/net/can/softing/softing_cs.c b/drivers/net/can/softing/softing_cs.c new file mode 100644 index 00000000000..300fe75dd1a --- /dev/null +++ b/drivers/net/can/softing/softing_cs.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * 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/module.h> +#include <linux/kernel.h> + +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> + +#include "softing_platform.h" + +static int softingcs_index; +static spinlock_t softingcs_index_lock; + +static int softingcs_reset(struct platform_device *pdev, int v); +static int softingcs_enable_irq(struct platform_device *pdev, int v); + +/* + * platform_data descriptions + */ +#define MHZ (1000*1000) +static const struct softing_platform_data softingcs_platform_data[] = { +{ + .name = "CANcard", + .manf = 0x0168, .prod = 0x001, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-NEC", + .manf = 0x0168, .prod = 0x002, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-SJA", + .manf = 0x0168, .prod = 0x004, + .generation = 1, + .nbus = 2, + .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "CANcard-2", + .manf = 0x0168, .prod = 0x005, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + .name = "Vector-CANcard", + .manf = 0x0168, .prod = 0x081, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "Vector-CANcard-SJA", + .manf = 0x0168, .prod = 0x084, + .generation = 1, + .nbus = 2, + .freq = 20 * MHZ, .max_brp = 32, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cansja.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "Vector-CANcard-2", + .manf = 0x0168, .prod = 0x085, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + .name = "EDICcard-NEC", + .manf = 0x0168, .prod = 0x102, + .generation = 1, + .nbus = 2, + .freq = 16 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x0800, + .boot = {0x0000, 0x000000, fw_dir "bcard.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancard.bin",}, + .reset = softingcs_reset, + .enable_irq = softingcs_enable_irq, +}, { + .name = "EDICcard-2", + .manf = 0x0168, .prod = 0x105, + .generation = 2, + .nbus = 2, + .freq = 24 * MHZ, .max_brp = 64, .max_sjw = 4, + .dpram_size = 0x1000, + .boot = {0x0000, 0x000000, fw_dir "bcard2.bin",}, + .load = {0x0120, 0x00f600, fw_dir "ldcard2.bin",}, + .app = {0x0010, 0x0d0000, fw_dir "cancrd2.bin",}, + .reset = softingcs_reset, + .enable_irq = NULL, +}, { + 0, 0, +}, +}; + +MODULE_FIRMWARE(fw_dir "bcard.bin"); +MODULE_FIRMWARE(fw_dir "ldcard.bin"); +MODULE_FIRMWARE(fw_dir "cancard.bin"); +MODULE_FIRMWARE(fw_dir "cansja.bin"); + +MODULE_FIRMWARE(fw_dir "bcard2.bin"); +MODULE_FIRMWARE(fw_dir "ldcard2.bin"); +MODULE_FIRMWARE(fw_dir "cancrd2.bin"); + +static __devinit const struct softing_platform_data +*softingcs_find_platform_data(unsigned int manf, unsigned int prod) +{ + const struct softing_platform_data *lp; + + for (lp = softingcs_platform_data; lp->manf; ++lp) { + if ((lp->manf == manf) && (lp->prod == prod)) + return lp; + } + return NULL; +} + +/* + * platformdata callbacks + */ +static int softingcs_reset(struct platform_device *pdev, int v) +{ + struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); + + dev_dbg(&pdev->dev, "pcmcia config [2] %02x\n", v ? 0 : 0x20); + return pcmcia_write_config_byte(pcmcia, 2, v ? 0 : 0x20); +} + +static int softingcs_enable_irq(struct platform_device *pdev, int v) +{ + struct pcmcia_device *pcmcia = to_pcmcia_dev(pdev->dev.parent); + + dev_dbg(&pdev->dev, "pcmcia config [0] %02x\n", v ? 0x60 : 0); + return pcmcia_write_config_byte(pcmcia, 0, v ? 0x60 : 0); +} + +/* + * pcmcia check + */ +static __devinit int softingcs_probe_config(struct pcmcia_device *pcmcia, + void *priv_data) +{ + struct softing_platform_data *pdat = priv_data; + struct resource *pres; + int memspeed = 0; + + WARN_ON(!pdat); + pres = pcmcia->resource[PCMCIA_IOMEM_0]; + if (resource_size(pres) < 0x1000) + return -ERANGE; + + pres->flags |= WIN_MEMORY_TYPE_CM | WIN_ENABLE; + if (pdat->generation < 2) { + pres->flags |= WIN_USE_WAIT | WIN_DATA_WIDTH_8; + memspeed = 3; + } else { + pres->flags |= WIN_DATA_WIDTH_16; + } + return pcmcia_request_window(pcmcia, pres, memspeed); +} + +static __devexit void softingcs_remove(struct pcmcia_device *pcmcia) +{ + struct platform_device *pdev = pcmcia->priv; + + /* free bits */ + platform_device_unregister(pdev); + /* release pcmcia stuff */ + pcmcia_disable_device(pcmcia); +} + +/* + * platform_device wrapper + * pdev->resource has 2 entries: io & irq + */ +static void softingcs_pdev_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + kfree(pdev); +} + +static __devinit int softingcs_probe(struct pcmcia_device *pcmcia) +{ + int ret; + struct platform_device *pdev; + const struct softing_platform_data *pdat; + struct resource *pres; + struct dev { + struct platform_device pdev; + struct resource res[2]; + } *dev; + + /* find matching platform_data */ + pdat = softingcs_find_platform_data(pcmcia->manf_id, pcmcia->card_id); + if (!pdat) + return -ENOTTY; + + /* setup pcmcia device */ + pcmcia->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IOMEM | + CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + ret = pcmcia_loop_config(pcmcia, softingcs_probe_config, (void *)pdat); + if (ret) + goto pcmcia_failed; + + ret = pcmcia_enable_device(pcmcia); + if (ret < 0) + goto pcmcia_failed; + + pres = pcmcia->resource[PCMCIA_IOMEM_0]; + if (!pres) { + ret = -EBADF; + goto pcmcia_bad; + } + + /* create softing platform device */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto mem_failed; + } + dev->pdev.resource = dev->res; + dev->pdev.num_resources = ARRAY_SIZE(dev->res); + dev->pdev.dev.release = softingcs_pdev_release; + + pdev = &dev->pdev; + pdev->dev.platform_data = (void *)pdat; + pdev->dev.parent = &pcmcia->dev; + pcmcia->priv = pdev; + + /* platform device resources */ + pdev->resource[0].flags = IORESOURCE_MEM; + pdev->resource[0].start = pres->start; + pdev->resource[0].end = pres->end; + + pdev->resource[1].flags = IORESOURCE_IRQ; + pdev->resource[1].start = pcmcia->irq; + pdev->resource[1].end = pdev->resource[1].start; + + /* platform device setup */ + spin_lock(&softingcs_index_lock); + pdev->id = softingcs_index++; + spin_unlock(&softingcs_index_lock); + pdev->name = "softing"; + dev_set_name(&pdev->dev, "softingcs.%i", pdev->id); + ret = platform_device_register(pdev); + if (ret < 0) + goto platform_failed; + + dev_info(&pcmcia->dev, "created %s\n", dev_name(&pdev->dev)); + return 0; + +platform_failed: + kfree(dev); +mem_failed: +pcmcia_bad: +pcmcia_failed: + pcmcia_disable_device(pcmcia); + pcmcia->priv = NULL; + return ret ?: -ENODEV; +} + +static /*const*/ struct pcmcia_device_id softingcs_ids[] = { + /* softing */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0004), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0005), + /* vector, manufacturer? */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0081), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0084), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0085), + /* EDIC */ + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0102), + PCMCIA_DEVICE_MANF_CARD(0x0168, 0x0105), + PCMCIA_DEVICE_NULL, +}; + +MODULE_DEVICE_TABLE(pcmcia, softingcs_ids); + +static struct pcmcia_driver softingcs_driver = { + .owner = THIS_MODULE, + .name = "softingcs", + .id_table = softingcs_ids, + .probe = softingcs_probe, + .remove = __devexit_p(softingcs_remove), +}; + +static int __init softingcs_start(void) +{ + spin_lock_init(&softingcs_index_lock); + return pcmcia_register_driver(&softingcs_driver); +} + +static void __exit softingcs_stop(void) +{ + pcmcia_unregister_driver(&softingcs_driver); +} + +module_init(softingcs_start); +module_exit(softingcs_stop); + +MODULE_DESCRIPTION("softing CANcard driver" + ", links PCMCIA card to softing driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c new file mode 100644 index 00000000000..b520784fb19 --- /dev/null +++ b/drivers/net/can/softing/softing_fw.c @@ -0,0 +1,691 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * 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/firmware.h> +#include <linux/sched.h> +#include <asm/div64.h> + +#include "softing.h" + +/* + * low level DPRAM command. + * Make sure that card->dpram[DPRAM_FCT_HOST] is preset + */ +static int _softing_fct_cmd(struct softing *card, int16_t cmd, uint16_t vector, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(cmd, &card->dpram[DPRAM_FCT_PARAM]); + iowrite8(vector >> 8, &card->dpram[DPRAM_FCT_HOST + 1]); + iowrite8(vector, &card->dpram[DPRAM_FCT_HOST]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 1 * HZ; + /* wait for card */ + do { + /* DPRAM_FCT_HOST is _not_ aligned */ + ret = ioread8(&card->dpram[DPRAM_FCT_HOST]) + + (ioread8(&card->dpram[DPRAM_FCT_HOST + 1]) << 8); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + /* read return-value now */ + return ioread16(&card->dpram[DPRAM_FCT_RESULT]); + + if ((ret != vector) || time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (1); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "firmware %s failed (%i)\n", msg, ret); + return ret; +} + +static int softing_fct_cmd(struct softing *card, int16_t cmd, const char *msg) +{ + int ret; + + ret = _softing_fct_cmd(card, cmd, 0, msg); + if (ret > 0) { + dev_alert(&card->pdev->dev, "%s returned %u\n", msg, ret); + ret = -EIO; + } + return ret; +} + +int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg) +{ + int ret; + unsigned long stamp; + + iowrite16(RES_NONE, &card->dpram[DPRAM_RECEIPT]); + iowrite16(cmd, &card->dpram[DPRAM_COMMAND]); + /* be sure to flush this to the card */ + wmb(); + stamp = jiffies + 3 * HZ; + /* wait for card */ + do { + ret = ioread16(&card->dpram[DPRAM_RECEIPT]); + /* don't have any cached variables */ + rmb(); + if (ret == RES_OK) + return 0; + if (time_after(jiffies, stamp)) + break; + /* process context => relax */ + usleep_range(500, 10000); + } while (!signal_pending(current)); + + ret = (ret == RES_NONE) ? -ETIMEDOUT : -ECANCELED; + dev_alert(&card->pdev->dev, "bootloader %s failed (%i)\n", msg, ret); + return ret; +} + +static int fw_parse(const uint8_t **pmem, uint16_t *ptype, uint32_t *paddr, + uint16_t *plen, const uint8_t **pdat) +{ + uint16_t checksum[2]; + const uint8_t *mem; + const uint8_t *end; + + /* + * firmware records are a binary, unaligned stream composed of: + * uint16_t type; + * uint32_t addr; + * uint16_t len; + * uint8_t dat[len]; + * uint16_t checksum; + * all values in little endian. + * We could define a struct for this, with __attribute__((packed)), + * but would that solve the alignment in _all_ cases (cfr. the + * struct itself may be an odd address)? + * + * I chose to use leXX_to_cpup() since this solves both + * endianness & alignment. + */ + mem = *pmem; + *ptype = le16_to_cpup((void *)&mem[0]); + *paddr = le32_to_cpup((void *)&mem[2]); + *plen = le16_to_cpup((void *)&mem[6]); + *pdat = &mem[8]; + /* verify checksum */ + end = &mem[8 + *plen]; + checksum[0] = le16_to_cpup((void *)end); + for (checksum[1] = 0; mem < end; ++mem) + checksum[1] += *mem; + if (checksum[0] != checksum[1]) + return -EINVAL; + /* increment */ + *pmem += 10 + *plen; + return 0; +} + +int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *dpram, unsigned int size, int offset) +{ + const struct firmware *fw; + int ret; + const uint8_t *mem, *end, *dat; + uint16_t type, len; + uint32_t addr; + uint8_t *buf = NULL; + int buflen = 0; + int8_t type_end = 0; + + ret = request_firmware(&fw, file, &card->pdev->dev); + if (ret < 0) + return ret; + dev_dbg(&card->pdev->dev, "%s, firmware(%s) got %u bytes" + ", offset %c0x%04x\n", + card->pdat->name, file, (unsigned int)fw->size, + (offset >= 0) ? '+' : '-', (unsigned int)abs(offset)); + /* parse the firmware */ + mem = fw->data; + end = &mem[fw->size]; + /* look for header record */ + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret < 0) + goto failed; + if (type != 0xffff) + goto failed; + if (strncmp("Structured Binary Format, Softing GmbH" , dat, len)) { + ret = -EINVAL; + goto failed; + } + /* ok, we had a header */ + while (mem < end) { + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret < 0) + goto failed; + if (type == 3) { + /* start address, not used here */ + continue; + } else if (type == 1) { + /* eof */ + type_end = 1; + break; + } else if (type != 0) { + ret = -EINVAL; + goto failed; + } + + if ((addr + len + offset) > size) + goto failed; + memcpy_toio(&dpram[addr + offset], dat, len); + /* be sure to flush caches from IO space */ + mb(); + if (len > buflen) { + /* align buflen */ + buflen = (len + (1024-1)) & ~(1024-1); + buf = krealloc(buf, buflen, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto failed; + } + } + /* verify record data */ + memcpy_fromio(buf, &dpram[addr + offset], len); + if (memcmp(buf, dat, len)) { + /* is not ok */ + dev_alert(&card->pdev->dev, "DPRAM readback failed\n"); + ret = -EIO; + goto failed; + } + } + if (!type_end) + /* no end record seen */ + goto failed; + ret = 0; +failed: + kfree(buf); + release_firmware(fw); + if (ret < 0) + dev_info(&card->pdev->dev, "firmware %s failed\n", file); + return ret; +} + +int softing_load_app_fw(const char *file, struct softing *card) +{ + const struct firmware *fw; + const uint8_t *mem, *end, *dat; + int ret, j; + uint16_t type, len; + uint32_t addr, start_addr = 0; + unsigned int sum, rx_sum; + int8_t type_end = 0, type_entrypoint = 0; + + ret = request_firmware(&fw, file, &card->pdev->dev); + if (ret) { + dev_alert(&card->pdev->dev, "request_firmware(%s) got %i\n", + file, ret); + return ret; + } + dev_dbg(&card->pdev->dev, "firmware(%s) got %lu bytes\n", + file, (unsigned long)fw->size); + /* parse the firmware */ + mem = fw->data; + end = &mem[fw->size]; + /* look for header record */ + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret) + goto failed; + ret = -EINVAL; + if (type != 0xffff) { + dev_alert(&card->pdev->dev, "firmware starts with type 0x%x\n", + type); + goto failed; + } + if (strncmp("Structured Binary Format, Softing GmbH", dat, len)) { + dev_alert(&card->pdev->dev, "firmware string '%.*s' fault\n", + len, dat); + goto failed; + } + /* ok, we had a header */ + while (mem < end) { + ret = fw_parse(&mem, &type, &addr, &len, &dat); + if (ret) + goto failed; + + if (type == 3) { + /* start address */ + start_addr = addr; + type_entrypoint = 1; + continue; + } else if (type == 1) { + /* eof */ + type_end = 1; + break; + } else if (type != 0) { + dev_alert(&card->pdev->dev, + "unknown record type 0x%04x\n", type); + ret = -EINVAL; + goto failed; + } + + /* regualar data */ + for (sum = 0, j = 0; j < len; ++j) + sum += dat[j]; + /* work in 16bit (target) */ + sum &= 0xffff; + + memcpy_toio(&card->dpram[card->pdat->app.offs], dat, len); + iowrite32(card->pdat->app.offs + card->pdat->app.addr, + &card->dpram[DPRAM_COMMAND + 2]); + iowrite32(addr, &card->dpram[DPRAM_COMMAND + 6]); + iowrite16(len, &card->dpram[DPRAM_COMMAND + 10]); + iowrite8(1, &card->dpram[DPRAM_COMMAND + 12]); + ret = softing_bootloader_command(card, 1, "loading app."); + if (ret < 0) + goto failed; + /* verify checksum */ + rx_sum = ioread16(&card->dpram[DPRAM_RECEIPT + 2]); + if (rx_sum != sum) { + dev_alert(&card->pdev->dev, "SRAM seems to be damaged" + ", wanted 0x%04x, got 0x%04x\n", sum, rx_sum); + ret = -EIO; + goto failed; + } + } + if (!type_end || !type_entrypoint) + goto failed; + /* start application in card */ + iowrite32(start_addr, &card->dpram[DPRAM_COMMAND + 2]); + iowrite8(1, &card->dpram[DPRAM_COMMAND + 6]); + ret = softing_bootloader_command(card, 3, "start app."); + if (ret < 0) + goto failed; + ret = 0; +failed: + release_firmware(fw); + if (ret < 0) + dev_info(&card->pdev->dev, "firmware %s failed\n", file); + return ret; +} + +static int softing_reset_chip(struct softing *card) +{ + int ret; + + do { + /* reset chip */ + iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO]); + iowrite8(0, &card->dpram[DPRAM_RESET_RX_FIFO+1]); + iowrite8(1, &card->dpram[DPRAM_RESET]); + iowrite8(0, &card->dpram[DPRAM_RESET+1]); + + ret = softing_fct_cmd(card, 0, "reset_can"); + if (!ret) + break; + if (signal_pending(current)) + /* don't wait any longer */ + break; + } while (1); + card->tx.pending = 0; + return ret; +} + +int softing_chip_poweron(struct softing *card) +{ + int ret; + /* sync */ + ret = _softing_fct_cmd(card, 99, 0x55, "sync-a"); + if (ret < 0) + goto failed; + + ret = _softing_fct_cmd(card, 99, 0xaa, "sync-b"); + if (ret < 0) + goto failed; + + ret = softing_reset_chip(card); + if (ret < 0) + goto failed; + /* get_serial */ + ret = softing_fct_cmd(card, 43, "get_serial_number"); + if (ret < 0) + goto failed; + card->id.serial = ioread32(&card->dpram[DPRAM_FCT_PARAM]); + /* get_version */ + ret = softing_fct_cmd(card, 12, "get_version"); + if (ret < 0) + goto failed; + card->id.fw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 2]); + card->id.hw_version = ioread16(&card->dpram[DPRAM_FCT_PARAM + 4]); + card->id.license = ioread16(&card->dpram[DPRAM_FCT_PARAM + 6]); + card->id.chip[0] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 8]); + card->id.chip[1] = ioread16(&card->dpram[DPRAM_FCT_PARAM + 10]); + return 0; +failed: + return ret; +} + +static void softing_initialize_timestamp(struct softing *card) +{ + uint64_t ovf; + + card->ts_ref = ktime_get(); + + /* 16MHz is the reference */ + ovf = 0x100000000ULL * 16; + do_div(ovf, card->pdat->freq ?: 16); + + card->ts_overflow = ktime_add_us(ktime_set(0, 0), ovf); +} + +ktime_t softing_raw2ktime(struct softing *card, u32 raw) +{ + uint64_t rawl; + ktime_t now, real_offset; + ktime_t target; + ktime_t tmp; + + now = ktime_get(); + real_offset = ktime_sub(ktime_get_real(), now); + + /* find nsec from card */ + rawl = raw * 16; + do_div(rawl, card->pdat->freq ?: 16); + target = ktime_add_us(card->ts_ref, rawl); + /* test for overflows */ + tmp = ktime_add(target, card->ts_overflow); + while (unlikely(ktime_to_ns(tmp) > ktime_to_ns(now))) { + card->ts_ref = ktime_add(card->ts_ref, card->ts_overflow); + target = tmp; + tmp = ktime_add(target, card->ts_overflow); + } + return ktime_add(target, real_offset); +} + +static inline int softing_error_reporting(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + + return (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + ? 1 : 0; +} + +int softing_startstop(struct net_device *dev, int up) +{ + int ret; + struct softing *card; + struct softing_priv *priv; + struct net_device *netdev; + int bus_bitmask_start; + int j, error_reporting; + struct can_frame msg; + const struct can_bittiming *bt; + + priv = netdev_priv(dev); + card = priv->card; + + if (!card->fw.up) + return -EIO; + + ret = mutex_lock_interruptible(&card->fw.lock); + if (ret) + return ret; + + bus_bitmask_start = 0; + if (dev && up) + /* prepare to start this bus as well */ + bus_bitmask_start |= (1 << priv->index); + /* bring netdevs down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + + if (dev != netdev) + netif_stop_queue(netdev); + + if (netif_running(netdev)) { + if (dev != netdev) + bus_bitmask_start |= (1 << j); + priv->tx.pending = 0; + priv->tx.echo_put = 0; + priv->tx.echo_get = 0; + /* + * this bus' may just have called open_candev() + * which is rather stupid to call close_candev() + * already + * but we may come here from busoff recovery too + * in which case the echo_skb _needs_ flushing too. + * just be sure to call open_candev() again + */ + close_candev(netdev); + } + priv->can.state = CAN_STATE_STOPPED; + } + card->tx.pending = 0; + + softing_enable_irq(card, 0); + ret = softing_reset_chip(card); + if (ret) + goto failed; + if (!bus_bitmask_start) + /* no busses to be brought up */ + goto card_done; + + if ((bus_bitmask_start & 1) && (bus_bitmask_start & 2) + && (softing_error_reporting(card->net[0]) + != softing_error_reporting(card->net[1]))) { + dev_alert(&card->pdev->dev, + "err_reporting flag differs for busses\n"); + goto invalid; + } + error_reporting = 0; + if (bus_bitmask_start & 1) { + netdev = card->net[0]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip 1 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 1, "initialize_chip[0]"); + if (ret < 0) + goto failed; + /* set mode */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 3, "set_mode[0]"); + if (ret < 0) + goto failed; + /* set filter */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 7, "set_filter[0]"); + if (ret < 0) + goto failed; + /* set output control */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 5, "set_output[0]"); + if (ret < 0) + goto failed; + } + if (bus_bitmask_start & 2) { + netdev = card->net[1]; + priv = netdev_priv(netdev); + error_reporting += softing_error_reporting(netdev); + /* init chip2 */ + bt = &priv->can.bittiming; + iowrite16(bt->brp, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(bt->sjw, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(bt->phase_seg1 + bt->prop_seg, + &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(bt->phase_seg2, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) ? 1 : 0, + &card->dpram[DPRAM_FCT_PARAM + 10]); + ret = softing_fct_cmd(card, 2, "initialize_chip[1]"); + if (ret < 0) + goto failed; + /* set mode2 */ + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0, &card->dpram[DPRAM_FCT_PARAM + 4]); + ret = softing_fct_cmd(card, 4, "set_mode[1]"); + if (ret < 0) + goto failed; + /* set filter2 */ + /* 11bit id & mask */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(0x07ff, &card->dpram[DPRAM_FCT_PARAM + 4]); + /* 29bit id.lo & mask.lo & id.hi & mask.hi */ + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(0xffff, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(0x0000, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(0x1fff, &card->dpram[DPRAM_FCT_PARAM + 12]); + ret = softing_fct_cmd(card, 8, "set_filter[1]"); + if (ret < 0) + goto failed; + /* set output control2 */ + iowrite16(priv->output, &card->dpram[DPRAM_FCT_PARAM + 2]); + ret = softing_fct_cmd(card, 6, "set_output[1]"); + if (ret < 0) + goto failed; + } + /* enable_error_frame */ + /* + * Error reporting is switched off at the moment since + * the receiving of them is not yet 100% verified + * This should be enabled sooner or later + * + if (error_reporting) { + ret = softing_fct_cmd(card, 51, "enable_error_frame"); + if (ret < 0) + goto failed; + } + */ + /* initialize interface */ + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 2]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 4]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 6]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 8]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 10]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 12]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 14]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 16]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 18]); + iowrite16(1, &card->dpram[DPRAM_FCT_PARAM + 20]); + ret = softing_fct_cmd(card, 17, "initialize_interface"); + if (ret < 0) + goto failed; + /* enable_fifo */ + ret = softing_fct_cmd(card, 36, "enable_fifo"); + if (ret < 0) + goto failed; + /* enable fifo tx ack */ + ret = softing_fct_cmd(card, 13, "fifo_tx_ack[0]"); + if (ret < 0) + goto failed; + /* enable fifo tx ack2 */ + ret = softing_fct_cmd(card, 14, "fifo_tx_ack[1]"); + if (ret < 0) + goto failed; + /* start_chip */ + ret = softing_fct_cmd(card, 11, "start_chip"); + if (ret < 0) + goto failed; + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE]); + iowrite8(0, &card->dpram[DPRAM_INFO_BUSSTATE2]); + if (card->pdat->generation < 2) { + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + /* flush the DPRAM caches */ + wmb(); + } + + softing_initialize_timestamp(card); + + /* + * do socketcan notifications/status changes + * from here, no errors should occur, or the failed: part + * must be reviewed + */ + memset(&msg, 0, sizeof(msg)); + msg.can_id = CAN_ERR_FLAG | CAN_ERR_RESTARTED; + msg.can_dlc = CAN_ERR_DLC; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!(bus_bitmask_start & (1 << j))) + continue; + netdev = card->net[j]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + open_candev(netdev); + if (dev != netdev) { + /* notify other busses on the restart */ + softing_netdev_rx(netdev, &msg, ktime_set(0, 0)); + ++priv->can.can_stats.restarts; + } + netif_wake_queue(netdev); + } + + /* enable interrupts */ + ret = softing_enable_irq(card, 1); + if (ret) + goto failed; +card_done: + mutex_unlock(&card->fw.lock); + return 0; +invalid: + ret = -EINVAL; +failed: + softing_enable_irq(card, 0); + softing_reset_chip(card); + mutex_unlock(&card->fw.lock); + /* bring all other interfaces down */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + dev_close(netdev); + } + return ret; +} + +int softing_default_output(struct net_device *netdev) +{ + struct softing_priv *priv = netdev_priv(netdev); + struct softing *card = priv->card; + + switch (priv->chip) { + case 1000: + return (card->pdat->generation < 2) ? 0xfb : 0xfa; + case 5: + return 0x60; + default: + return 0x40; + } +} diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c new file mode 100644 index 00000000000..5157e15e96e --- /dev/null +++ b/drivers/net/can/softing/softing_main.c @@ -0,0 +1,893 @@ +/* + * Copyright (C) 2008-2010 + * + * - Kurt Van Dijck, EIA Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * 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/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> + +#include "softing.h" + +#define TX_ECHO_SKB_MAX (((TXMAX+1)/2)-1) + +/* + * test is a specific CAN netdev + * is online (ie. up 'n running, not sleeping, not busoff + */ +static inline int canif_is_active(struct net_device *netdev) +{ + struct can_priv *can = netdev_priv(netdev); + + if (!netif_running(netdev)) + return 0; + return (can->state <= CAN_STATE_ERROR_PASSIVE); +} + +/* reset DPRAM */ +static inline void softing_set_reset_dpram(struct softing *card) +{ + if (card->pdat->generation >= 2) { + spin_lock_bh(&card->spin); + iowrite8(ioread8(&card->dpram[DPRAM_V2_RESET]) & ~1, + &card->dpram[DPRAM_V2_RESET]); + spin_unlock_bh(&card->spin); + } +} + +static inline void softing_clr_reset_dpram(struct softing *card) +{ + if (card->pdat->generation >= 2) { + spin_lock_bh(&card->spin); + iowrite8(ioread8(&card->dpram[DPRAM_V2_RESET]) | 1, + &card->dpram[DPRAM_V2_RESET]); + spin_unlock_bh(&card->spin); + } +} + +/* trigger the tx queue-ing */ +static netdev_tx_t softing_netdev_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct softing_priv *priv = netdev_priv(dev); + struct softing *card = priv->card; + int ret; + uint8_t *ptr; + uint8_t fifo_wr, fifo_rd; + struct can_frame *cf = (struct can_frame *)skb->data; + uint8_t buf[DPRAM_TX_SIZE]; + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + spin_lock(&card->spin); + + ret = NETDEV_TX_BUSY; + if (!card->fw.up || + (card->tx.pending >= TXMAX) || + (priv->tx.pending >= TX_ECHO_SKB_MAX)) + goto xmit_done; + fifo_wr = ioread8(&card->dpram[DPRAM_TX_WR]); + fifo_rd = ioread8(&card->dpram[DPRAM_TX_RD]); + if (fifo_wr == fifo_rd) + /* fifo full */ + goto xmit_done; + memset(buf, 0, sizeof(buf)); + ptr = buf; + *ptr = CMD_TX; + if (cf->can_id & CAN_RTR_FLAG) + *ptr |= CMD_RTR; + if (cf->can_id & CAN_EFF_FLAG) + *ptr |= CMD_XTD; + if (priv->index) + *ptr |= CMD_BUS2; + ++ptr; + *ptr++ = cf->can_dlc; + *ptr++ = (cf->can_id >> 0); + *ptr++ = (cf->can_id >> 8); + if (cf->can_id & CAN_EFF_FLAG) { + *ptr++ = (cf->can_id >> 16); + *ptr++ = (cf->can_id >> 24); + } else { + /* increment 1, not 2 as you might think */ + ptr += 1; + } + if (!(cf->can_id & CAN_RTR_FLAG)) + memcpy(ptr, &cf->data[0], cf->can_dlc); + memcpy_toio(&card->dpram[DPRAM_TX + DPRAM_TX_SIZE * fifo_wr], + buf, DPRAM_TX_SIZE); + if (++fifo_wr >= DPRAM_TX_CNT) + fifo_wr = 0; + iowrite8(fifo_wr, &card->dpram[DPRAM_TX_WR]); + card->tx.last_bus = priv->index; + ++card->tx.pending; + ++priv->tx.pending; + can_put_echo_skb(skb, dev, priv->tx.echo_put); + ++priv->tx.echo_put; + if (priv->tx.echo_put >= TX_ECHO_SKB_MAX) + priv->tx.echo_put = 0; + /* can_put_echo_skb() saves the skb, safe to return TX_OK */ + ret = NETDEV_TX_OK; +xmit_done: + spin_unlock(&card->spin); + if (card->tx.pending >= TXMAX) { + int j; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (card->net[j]) + netif_stop_queue(card->net[j]); + } + } + if (ret != NETDEV_TX_OK) + netif_stop_queue(dev); + + return ret; +} + +/* + * shortcut for skb delivery + */ +int softing_netdev_rx(struct net_device *netdev, const struct can_frame *msg, + ktime_t ktime) +{ + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_skb(netdev, &cf); + if (!skb) + return -ENOMEM; + memcpy(cf, msg, sizeof(*msg)); + skb->tstamp = ktime; + return netif_rx(skb); +} + +/* + * softing_handle_1 + * pop 1 entry from the DPRAM queue, and process + */ +static int softing_handle_1(struct softing *card) +{ + struct net_device *netdev; + struct softing_priv *priv; + ktime_t ktime; + struct can_frame msg; + int cnt = 0, lost_msg; + uint8_t fifo_rd, fifo_wr, cmd; + uint8_t *ptr; + uint32_t tmp_u32; + uint8_t buf[DPRAM_RX_SIZE]; + + memset(&msg, 0, sizeof(msg)); + /* test for lost msgs */ + lost_msg = ioread8(&card->dpram[DPRAM_RX_LOST]); + if (lost_msg) { + int j; + /* reset condition */ + iowrite8(0, &card->dpram[DPRAM_RX_LOST]); + /* prepare msg */ + msg.can_id = CAN_ERR_FLAG | CAN_ERR_CRTL; + msg.can_dlc = CAN_ERR_DLC; + msg.data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + /* + * service to all busses, we don't know which it was applicable + * but only service busses that are online + */ + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + netdev = card->net[j]; + if (!netdev) + continue; + if (!canif_is_active(netdev)) + /* a dead bus has no overflows */ + continue; + ++netdev->stats.rx_over_errors; + softing_netdev_rx(netdev, &msg, ktime_set(0, 0)); + } + /* prepare for other use */ + memset(&msg, 0, sizeof(msg)); + ++cnt; + } + + fifo_rd = ioread8(&card->dpram[DPRAM_RX_RD]); + fifo_wr = ioread8(&card->dpram[DPRAM_RX_WR]); + + if (++fifo_rd >= DPRAM_RX_CNT) + fifo_rd = 0; + if (fifo_wr == fifo_rd) + return cnt; + + memcpy_fromio(buf, &card->dpram[DPRAM_RX + DPRAM_RX_SIZE*fifo_rd], + DPRAM_RX_SIZE); + mb(); + /* trigger dual port RAM */ + iowrite8(fifo_rd, &card->dpram[DPRAM_RX_RD]); + + ptr = buf; + cmd = *ptr++; + if (cmd == 0xff) + /* not quite usefull, probably the card has got out */ + return 0; + netdev = card->net[0]; + if (cmd & CMD_BUS2) + netdev = card->net[1]; + priv = netdev_priv(netdev); + + if (cmd & CMD_ERR) { + uint8_t can_state, state; + + state = *ptr++; + + msg.can_id = CAN_ERR_FLAG; + msg.can_dlc = CAN_ERR_DLC; + + if (state & SF_MASK_BUSOFF) { + can_state = CAN_STATE_BUS_OFF; + msg.can_id |= CAN_ERR_BUSOFF; + state = STATE_BUSOFF; + } else if (state & SF_MASK_EPASSIVE) { + can_state = CAN_STATE_ERROR_PASSIVE; + msg.can_id |= CAN_ERR_CRTL; + msg.data[1] = CAN_ERR_CRTL_TX_PASSIVE; + state = STATE_EPASSIVE; + } else { + can_state = CAN_STATE_ERROR_ACTIVE; + msg.can_id |= CAN_ERR_CRTL; + state = STATE_EACTIVE; + } + /* update DPRAM */ + iowrite8(state, &card->dpram[priv->index ? + DPRAM_INFO_BUSSTATE2 : DPRAM_INFO_BUSSTATE]); + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + + ++netdev->stats.rx_errors; + /* update internal status */ + if (can_state != priv->can.state) { + priv->can.state = can_state; + if (can_state == CAN_STATE_ERROR_PASSIVE) + ++priv->can.can_stats.error_passive; + else if (can_state == CAN_STATE_BUS_OFF) { + /* this calls can_close_cleanup() */ + can_bus_off(netdev); + netif_stop_queue(netdev); + } + /* trigger socketcan */ + softing_netdev_rx(netdev, &msg, ktime); + } + + } else { + if (cmd & CMD_RTR) + msg.can_id |= CAN_RTR_FLAG; + msg.can_dlc = get_can_dlc(*ptr++); + if (cmd & CMD_XTD) { + msg.can_id |= CAN_EFF_FLAG; + msg.can_id |= le32_to_cpup((void *)ptr); + ptr += 4; + } else { + msg.can_id |= le16_to_cpup((void *)ptr); + ptr += 2; + } + /* timestamp */ + tmp_u32 = le32_to_cpup((void *)ptr); + ptr += 4; + ktime = softing_raw2ktime(card, tmp_u32); + if (!(msg.can_id & CAN_RTR_FLAG)) + memcpy(&msg.data[0], ptr, 8); + ptr += 8; + /* update socket */ + if (cmd & CMD_ACK) { + /* acknowledge, was tx msg */ + struct sk_buff *skb; + skb = priv->can.echo_skb[priv->tx.echo_get]; + if (skb) + skb->tstamp = ktime; + can_get_echo_skb(netdev, priv->tx.echo_get); + ++priv->tx.echo_get; + if (priv->tx.echo_get >= TX_ECHO_SKB_MAX) + priv->tx.echo_get = 0; + if (priv->tx.pending) + --priv->tx.pending; + if (card->tx.pending) + --card->tx.pending; + ++netdev->stats.tx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.tx_bytes += msg.can_dlc; + } else { + int ret; + + ret = softing_netdev_rx(netdev, &msg, ktime); + if (ret == NET_RX_SUCCESS) { + ++netdev->stats.rx_packets; + if (!(msg.can_id & CAN_RTR_FLAG)) + netdev->stats.rx_bytes += msg.can_dlc; + } else { + ++netdev->stats.rx_dropped; + } + } + } + ++cnt; + return cnt; +} + +/* + * real interrupt handler + */ +static irqreturn_t softing_irq_thread(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + struct net_device *netdev; + struct softing_priv *priv; + int j, offset, work_done; + + work_done = 0; + spin_lock_bh(&card->spin); + while (softing_handle_1(card) > 0) { + ++card->irq.svc_count; + ++work_done; + } + spin_unlock_bh(&card->spin); + /* resume tx queue's */ + offset = card->tx.last_bus; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (card->tx.pending >= TXMAX) + break; + netdev = card->net[(j + offset + 1) % card->pdat->nbus]; + if (!netdev) + continue; + priv = netdev_priv(netdev); + if (!canif_is_active(netdev)) + /* it makes no sense to wake dead busses */ + continue; + if (priv->tx.pending >= TX_ECHO_SKB_MAX) + continue; + ++work_done; + netif_wake_queue(netdev); + } + return work_done ? IRQ_HANDLED : IRQ_NONE; +} + +/* + * interrupt routines: + * schedule the 'real interrupt handler' + */ +static irqreturn_t softing_irq_v2(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_V2_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_V2_IRQ_TOHOST]); + return (1 == ir) ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +static irqreturn_t softing_irq_v1(int irq, void *dev_id) +{ + struct softing *card = (struct softing *)dev_id; + uint8_t ir; + + ir = ioread8(&card->dpram[DPRAM_IRQ_TOHOST]); + iowrite8(0, &card->dpram[DPRAM_IRQ_TOHOST]); + return ir ? IRQ_WAKE_THREAD : IRQ_NONE; +} + +/* + * netdev/candev inter-operability + */ +static int softing_netdev_open(struct net_device *ndev) +{ + int ret; + + /* check or determine and set bittime */ + ret = open_candev(ndev); + if (!ret) + ret = softing_startstop(ndev, 1); + return ret; +} + +static int softing_netdev_stop(struct net_device *ndev) +{ + int ret; + + netif_stop_queue(ndev); + + /* softing cycle does close_candev() */ + ret = softing_startstop(ndev, 0); + return ret; +} + +static int softing_candev_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret; + + switch (mode) { + case CAN_MODE_START: + /* softing_startstop does close_candev() */ + ret = softing_startstop(ndev, 1); + return ret; + case CAN_MODE_STOP: + case CAN_MODE_SLEEP: + return -EOPNOTSUPP; + } + return 0; +} + +/* + * Softing device management helpers + */ +int softing_enable_irq(struct softing *card, int enable) +{ + int ret; + + if (!card->irq.nr) { + return 0; + } else if (card->irq.requested && !enable) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } else if (!card->irq.requested && enable) { + ret = request_threaded_irq(card->irq.nr, + (card->pdat->generation >= 2) ? + softing_irq_v2 : softing_irq_v1, + softing_irq_thread, IRQF_SHARED, + dev_name(&card->pdev->dev), card); + if (ret) { + dev_alert(&card->pdev->dev, + "request_threaded_irq(%u) failed\n", + card->irq.nr); + return ret; + } + card->irq.requested = 1; + } + return 0; +} + +static void softing_card_shutdown(struct softing *card) +{ + int fw_up = 0; + + if (mutex_lock_interruptible(&card->fw.lock)) + /* return -ERESTARTSYS */; + fw_up = card->fw.up; + card->fw.up = 0; + + if (card->irq.requested && card->irq.nr) { + free_irq(card->irq.nr, card); + card->irq.requested = 0; + } + if (fw_up) { + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 0); + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + } + mutex_unlock(&card->fw.lock); +} + +static __devinit int softing_card_boot(struct softing *card) +{ + int ret, j; + static const uint8_t stream[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }; + unsigned char back[sizeof(stream)]; + + if (mutex_lock_interruptible(&card->fw.lock)) + return -ERESTARTSYS; + if (card->fw.up) { + mutex_unlock(&card->fw.lock); + return 0; + } + /* reset board */ + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 1); + /* boot card */ + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + for (j = 0; (j + sizeof(stream)) < card->dpram_size; + j += sizeof(stream)) { + + memcpy_toio(&card->dpram[j], stream, sizeof(stream)); + /* flush IO cache */ + mb(); + memcpy_fromio(back, &card->dpram[j], sizeof(stream)); + + if (!memcmp(back, stream, sizeof(stream))) + continue; + /* memory is not equal */ + dev_alert(&card->pdev->dev, "dpram failed at 0x%04x\n", j); + ret = -EIO; + goto failed; + } + wmb(); + /* load boot firmware */ + ret = softing_load_fw(card->pdat->boot.fw, card, card->dpram, + card->dpram_size, + card->pdat->boot.offs - card->pdat->boot.addr); + if (ret < 0) + goto failed; + /* load loader firmware */ + ret = softing_load_fw(card->pdat->load.fw, card, card->dpram, + card->dpram_size, + card->pdat->load.offs - card->pdat->load.addr); + if (ret < 0) + goto failed; + + if (card->pdat->reset) + card->pdat->reset(card->pdev, 0); + softing_clr_reset_dpram(card); + ret = softing_bootloader_command(card, 0, "card boot"); + if (ret < 0) + goto failed; + ret = softing_load_app_fw(card->pdat->app.fw, card); + if (ret < 0) + goto failed; + + ret = softing_chip_poweron(card); + if (ret < 0) + goto failed; + + card->fw.up = 1; + mutex_unlock(&card->fw.lock); + return 0; +failed: + card->fw.up = 0; + if (card->pdat->enable_irq) + card->pdat->enable_irq(card->pdev, 0); + softing_set_reset_dpram(card); + if (card->pdat->reset) + card->pdat->reset(card->pdev, 1); + mutex_unlock(&card->fw.lock); + return ret; +} + +/* + * netdev sysfs + */ +static ssize_t show_channel(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "%i\n", priv->index); +} + +static ssize_t show_chip(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "%i\n", priv->chip); +} + +static ssize_t show_output(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + + return sprintf(buf, "0x%02x\n", priv->output); +} + +static ssize_t store_output(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct net_device *ndev = to_net_dev(dev); + struct softing_priv *priv = netdev2softing(ndev); + struct softing *card = priv->card; + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 0, &val); + if (ret < 0) + return ret; + val &= 0xFF; + + ret = mutex_lock_interruptible(&card->fw.lock); + if (ret) + return -ERESTARTSYS; + if (netif_running(ndev)) { + mutex_unlock(&card->fw.lock); + return -EBUSY; + } + priv->output = val; + mutex_unlock(&card->fw.lock); + return count; +} + +static const DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); +static const DEVICE_ATTR(chip, S_IRUGO, show_chip, NULL); +static const DEVICE_ATTR(output, S_IRUGO | S_IWUSR, show_output, store_output); + +static const struct attribute *const netdev_sysfs_attrs[] = { + &dev_attr_channel.attr, + &dev_attr_chip.attr, + &dev_attr_output.attr, + NULL, +}; +static const struct attribute_group netdev_sysfs_group = { + .name = NULL, + .attrs = (struct attribute **)netdev_sysfs_attrs, +}; + +static const struct net_device_ops softing_netdev_ops = { + .ndo_open = softing_netdev_open, + .ndo_stop = softing_netdev_stop, + .ndo_start_xmit = softing_netdev_start_xmit, +}; + +static const struct can_bittiming_const softing_btr_const = { + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, /* overruled */ + .brp_min = 1, + .brp_max = 32, /* overruled */ + .brp_inc = 1, +}; + + +static __devinit struct net_device *softing_netdev_create(struct softing *card, + uint16_t chip_id) +{ + struct net_device *netdev; + struct softing_priv *priv; + + netdev = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX); + if (!netdev) { + dev_alert(&card->pdev->dev, "alloc_candev failed\n"); + return NULL; + } + priv = netdev_priv(netdev); + priv->netdev = netdev; + priv->card = card; + memcpy(&priv->btr_const, &softing_btr_const, sizeof(priv->btr_const)); + priv->btr_const.brp_max = card->pdat->max_brp; + priv->btr_const.sjw_max = card->pdat->max_sjw; + priv->can.bittiming_const = &priv->btr_const; + priv->can.clock.freq = 8000000; + priv->chip = chip_id; + priv->output = softing_default_output(netdev); + SET_NETDEV_DEV(netdev, &card->pdev->dev); + + netdev->flags |= IFF_ECHO; + netdev->netdev_ops = &softing_netdev_ops; + priv->can.do_set_mode = softing_candev_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + + return netdev; +} + +static __devinit int softing_netdev_register(struct net_device *netdev) +{ + int ret; + + netdev->sysfs_groups[0] = &netdev_sysfs_group; + ret = register_candev(netdev); + if (ret) { + dev_alert(&netdev->dev, "register failed\n"); + return ret; + } + return 0; +} + +static void softing_netdev_cleanup(struct net_device *netdev) +{ + unregister_candev(netdev); + free_candev(netdev); +} + +/* + * sysfs for Platform device + */ +#define DEV_ATTR_RO(name, member) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + return sprintf(buf, "%u\n", card->member); \ +} \ +static DEVICE_ATTR(name, 0444, show_##name, NULL) + +#define DEV_ATTR_RO_STR(name, member) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct softing *card = platform_get_drvdata(to_platform_device(dev)); \ + return sprintf(buf, "%s\n", card->member); \ +} \ +static DEVICE_ATTR(name, 0444, show_##name, NULL) + +DEV_ATTR_RO(serial, id.serial); +DEV_ATTR_RO_STR(firmware, pdat->app.fw); +DEV_ATTR_RO(firmware_version, id.fw_version); +DEV_ATTR_RO_STR(hardware, pdat->name); +DEV_ATTR_RO(hardware_version, id.hw_version); +DEV_ATTR_RO(license, id.license); +DEV_ATTR_RO(frequency, id.freq); +DEV_ATTR_RO(txpending, tx.pending); + +static struct attribute *softing_pdev_attrs[] = { + &dev_attr_serial.attr, + &dev_attr_firmware.attr, + &dev_attr_firmware_version.attr, + &dev_attr_hardware.attr, + &dev_attr_hardware_version.attr, + &dev_attr_license.attr, + &dev_attr_frequency.attr, + &dev_attr_txpending.attr, + NULL, +}; + +static const struct attribute_group softing_pdev_group = { + .name = NULL, + .attrs = softing_pdev_attrs, +}; + +/* + * platform driver + */ +static __devexit int softing_pdev_remove(struct platform_device *pdev) +{ + struct softing *card = platform_get_drvdata(pdev); + int j; + + /* first, disable card*/ + softing_card_shutdown(card); + + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!card->net[j]) + continue; + softing_netdev_cleanup(card->net[j]); + card->net[j] = NULL; + } + sysfs_remove_group(&pdev->dev.kobj, &softing_pdev_group); + + iounmap(card->dpram); + kfree(card); + return 0; +} + +static __devinit int softing_pdev_probe(struct platform_device *pdev) +{ + const struct softing_platform_data *pdat = pdev->dev.platform_data; + struct softing *card; + struct net_device *netdev; + struct softing_priv *priv; + struct resource *pres; + int ret; + int j; + + if (!pdat) { + dev_warn(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + if (pdat->nbus > ARRAY_SIZE(card->net)) { + dev_warn(&pdev->dev, "%u nets??\n", pdat->nbus); + return -EINVAL; + } + + card = kzalloc(sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + card->pdat = pdat; + card->pdev = pdev; + platform_set_drvdata(pdev, card); + mutex_init(&card->fw.lock); + spin_lock_init(&card->spin); + + ret = -EINVAL; + pres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!pres) + goto platform_resource_failed;; + card->dpram_phys = pres->start; + card->dpram_size = pres->end - pres->start + 1; + card->dpram = ioremap_nocache(card->dpram_phys, card->dpram_size); + if (!card->dpram) { + dev_alert(&card->pdev->dev, "dpram ioremap failed\n"); + goto ioremap_failed; + } + + pres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (pres) + card->irq.nr = pres->start; + + /* reset card */ + ret = softing_card_boot(card); + if (ret < 0) { + dev_alert(&pdev->dev, "failed to boot\n"); + goto boot_failed; + } + + /* only now, the chip's are known */ + card->id.freq = card->pdat->freq; + + ret = sysfs_create_group(&pdev->dev.kobj, &softing_pdev_group); + if (ret < 0) { + dev_alert(&card->pdev->dev, "sysfs failed\n"); + goto sysfs_failed; + } + + ret = -ENOMEM; + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + card->net[j] = netdev = + softing_netdev_create(card, card->id.chip[j]); + if (!netdev) { + dev_alert(&pdev->dev, "failed to make can[%i]", j); + goto netdev_failed; + } + priv = netdev_priv(card->net[j]); + priv->index = j; + ret = softing_netdev_register(netdev); + if (ret) { + free_candev(netdev); + card->net[j] = NULL; + dev_alert(&card->pdev->dev, + "failed to register can[%i]\n", j); + goto netdev_failed; + } + } + dev_info(&card->pdev->dev, "%s ready.\n", card->pdat->name); + return 0; + +netdev_failed: + for (j = 0; j < ARRAY_SIZE(card->net); ++j) { + if (!card->net[j]) + continue; + softing_netdev_cleanup(card->net[j]); + } + sysfs_remove_group(&pdev->dev.kobj, &softing_pdev_group); +sysfs_failed: + softing_card_shutdown(card); +boot_failed: + iounmap(card->dpram); +ioremap_failed: +platform_resource_failed: + kfree(card); + return ret; +} + +static struct platform_driver softing_driver = { + .driver = { + .name = "softing", + .owner = THIS_MODULE, + }, + .probe = softing_pdev_probe, + .remove = __devexit_p(softing_pdev_remove), +}; + +MODULE_ALIAS("platform:softing"); + +static int __init softing_start(void) +{ + return platform_driver_register(&softing_driver); +} + +static void __exit softing_stop(void) +{ + platform_driver_unregister(&softing_driver); +} + +module_init(softing_start); +module_exit(softing_stop); + +MODULE_DESCRIPTION("Softing DPRAM CAN driver"); +MODULE_AUTHOR("Kurt Van Dijck <kurt.van.dijck@eia.be>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/softing/softing_platform.h b/drivers/net/can/softing/softing_platform.h new file mode 100644 index 00000000000..ebbf6981562 --- /dev/null +++ b/drivers/net/can/softing/softing_platform.h @@ -0,0 +1,40 @@ + +#include <linux/platform_device.h> + +#ifndef _SOFTING_DEVICE_H_ +#define _SOFTING_DEVICE_H_ + +/* softing firmware directory prefix */ +#define fw_dir "softing-4.6/" + +struct softing_platform_data { + unsigned int manf; + unsigned int prod; + /* + * generation + * 1st with NEC or SJA1000 + * 8bit, exclusive interrupt, ... + * 2nd only SJA1000 + * 16bit, shared interrupt + */ + int generation; + int nbus; /* # busses on device */ + unsigned int freq; /* operating frequency in Hz */ + unsigned int max_brp; + unsigned int max_sjw; + unsigned long dpram_size; + const char *name; + struct { + unsigned long offs; + unsigned long addr; + const char *fw; + } boot, load, app; + /* + * reset() function + * bring pdev in or out of reset, depending on value + */ + int (*reset)(struct platform_device *pdev, int value); + int (*enable_irq)(struct platform_device *pdev, int value); +}; + +#endif diff --git a/drivers/net/cxgb4/cxgb4_main.c b/drivers/net/cxgb4/cxgb4_main.c index 059c1eec8c3..ec35d458102 100644 --- a/drivers/net/cxgb4/cxgb4_main.c +++ b/drivers/net/cxgb4/cxgb4_main.c @@ -2710,6 +2710,8 @@ static int cxgb_open(struct net_device *dev) struct port_info *pi = netdev_priv(dev); struct adapter *adapter = pi->adapter; + netif_carrier_off(dev); + if (!(adapter->flags & FULL_INIT_DONE)) { err = cxgb_up(adapter); if (err < 0) @@ -3661,7 +3663,6 @@ static int __devinit init_one(struct pci_dev *pdev, pi->xact_addr_filt = -1; pi->rx_offload = RX_CSO; pi->port_id = i; - netif_carrier_off(netdev); netdev->irq = pdev->irq; netdev->features |= NETIF_F_SG | TSO_FLAGS; diff --git a/drivers/net/e1000e/e1000.h b/drivers/net/e1000e/e1000.h index e610e136905..00bf595ebd6 100644 --- a/drivers/net/e1000e/e1000.h +++ b/drivers/net/e1000e/e1000.h @@ -364,6 +364,7 @@ struct e1000_adapter { /* structs defined in e1000_hw.h */ struct e1000_hw hw; + spinlock_t stats64_lock; struct e1000_hw_stats stats; struct e1000_phy_info phy_info; struct e1000_phy_stats phy_stats; @@ -494,7 +495,9 @@ extern int e1000e_setup_rx_resources(struct e1000_adapter *adapter); extern int e1000e_setup_tx_resources(struct e1000_adapter *adapter); extern void e1000e_free_rx_resources(struct e1000_adapter *adapter); extern void e1000e_free_tx_resources(struct e1000_adapter *adapter); -extern void e1000e_update_stats(struct e1000_adapter *adapter); +extern struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 + *stats); extern void e1000e_set_interrupt_capability(struct e1000_adapter *adapter); extern void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter); extern void e1000e_get_hw_control(struct e1000_adapter *adapter); diff --git a/drivers/net/e1000e/ethtool.c b/drivers/net/e1000e/ethtool.c index fa08b6336cf..daa7fe4b9fd 100644 --- a/drivers/net/e1000e/ethtool.c +++ b/drivers/net/e1000e/ethtool.c @@ -46,15 +46,15 @@ struct e1000_stats { }; #define E1000_STAT(str, m) { \ - .stat_string = str, \ - .type = E1000_STATS, \ - .sizeof_stat = sizeof(((struct e1000_adapter *)0)->m), \ - .stat_offset = offsetof(struct e1000_adapter, m) } + .stat_string = str, \ + .type = E1000_STATS, \ + .sizeof_stat = sizeof(((struct e1000_adapter *)0)->m), \ + .stat_offset = offsetof(struct e1000_adapter, m) } #define E1000_NETDEV_STAT(str, m) { \ - .stat_string = str, \ - .type = NETDEV_STATS, \ - .sizeof_stat = sizeof(((struct net_device *)0)->m), \ - .stat_offset = offsetof(struct net_device, m) } + .stat_string = str, \ + .type = NETDEV_STATS, \ + .sizeof_stat = sizeof(((struct rtnl_link_stats64 *)0)->m), \ + .stat_offset = offsetof(struct rtnl_link_stats64, m) } static const struct e1000_stats e1000_gstrings_stats[] = { E1000_STAT("rx_packets", stats.gprc), @@ -65,21 +65,21 @@ static const struct e1000_stats e1000_gstrings_stats[] = { E1000_STAT("tx_broadcast", stats.bptc), E1000_STAT("rx_multicast", stats.mprc), E1000_STAT("tx_multicast", stats.mptc), - E1000_NETDEV_STAT("rx_errors", stats.rx_errors), - E1000_NETDEV_STAT("tx_errors", stats.tx_errors), - E1000_NETDEV_STAT("tx_dropped", stats.tx_dropped), + E1000_NETDEV_STAT("rx_errors", rx_errors), + E1000_NETDEV_STAT("tx_errors", tx_errors), + E1000_NETDEV_STAT("tx_dropped", tx_dropped), E1000_STAT("multicast", stats.mprc), E1000_STAT("collisions", stats.colc), - E1000_NETDEV_STAT("rx_length_errors", stats.rx_length_errors), - E1000_NETDEV_STAT("rx_over_errors", stats.rx_over_errors), + E1000_NETDEV_STAT("rx_length_errors", rx_length_errors), + E1000_NETDEV_STAT("rx_over_errors", rx_over_errors), E1000_STAT("rx_crc_errors", stats.crcerrs), - E1000_NETDEV_STAT("rx_frame_errors", stats.rx_frame_errors), + E1000_NETDEV_STAT("rx_frame_errors", rx_frame_errors), E1000_STAT("rx_no_buffer_count", stats.rnbc), E1000_STAT("rx_missed_errors", stats.mpc), E1000_STAT("tx_aborted_errors", stats.ecol), E1000_STAT("tx_carrier_errors", stats.tncrs), - E1000_NETDEV_STAT("tx_fifo_errors", stats.tx_fifo_errors), - E1000_NETDEV_STAT("tx_heartbeat_errors", stats.tx_heartbeat_errors), + E1000_NETDEV_STAT("tx_fifo_errors", tx_fifo_errors), + E1000_NETDEV_STAT("tx_heartbeat_errors", tx_heartbeat_errors), E1000_STAT("tx_window_errors", stats.latecol), E1000_STAT("tx_abort_late_coll", stats.latecol), E1000_STAT("tx_deferred_ok", stats.dc), @@ -684,20 +684,13 @@ static int e1000_set_ringparam(struct net_device *netdev, rx_old = adapter->rx_ring; err = -ENOMEM; - tx_ring = kzalloc(sizeof(struct e1000_ring), GFP_KERNEL); + tx_ring = kmemdup(tx_old, sizeof(struct e1000_ring), GFP_KERNEL); if (!tx_ring) goto err_alloc_tx; - /* - * use a memcpy to save any previously configured - * items like napi structs from having to be - * reinitialized - */ - memcpy(tx_ring, tx_old, sizeof(struct e1000_ring)); - rx_ring = kzalloc(sizeof(struct e1000_ring), GFP_KERNEL); + rx_ring = kmemdup(rx_old, sizeof(struct e1000_ring), GFP_KERNEL); if (!rx_ring) goto err_alloc_rx; - memcpy(rx_ring, rx_old, sizeof(struct e1000_ring)); adapter->tx_ring = tx_ring; adapter->rx_ring = rx_ring; @@ -1255,7 +1248,6 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; u32 ctrl_reg = 0; - u32 stat_reg = 0; u16 phy_reg = 0; s32 ret_val = 0; @@ -1363,8 +1355,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) * Set the ILOS bit on the fiber Nic if half duplex link is * detected. */ - stat_reg = er32(STATUS); - if ((stat_reg & E1000_STATUS_FD) == 0) + if ((er32(STATUS) & E1000_STATUS_FD) == 0) ctrl_reg |= (E1000_CTRL_ILOS | E1000_CTRL_SLU); } @@ -1982,14 +1973,15 @@ static void e1000_get_ethtool_stats(struct net_device *netdev, u64 *data) { struct e1000_adapter *adapter = netdev_priv(netdev); + struct rtnl_link_stats64 net_stats; int i; char *p = NULL; - e1000e_update_stats(adapter); + e1000e_get_stats64(netdev, &net_stats); for (i = 0; i < E1000_GLOBAL_STATS_LEN; i++) { switch (e1000_gstrings_stats[i].type) { case NETDEV_STATS: - p = (char *) netdev + + p = (char *) &net_stats + e1000_gstrings_stats[i].stat_offset; break; case E1000_STATS: diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c index fb46974cfec..232b42b7f7c 100644 --- a/drivers/net/e1000e/ich8lan.c +++ b/drivers/net/e1000e/ich8lan.c @@ -2104,7 +2104,6 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) { union ich8_hws_flash_status hsfsts; s32 ret_val = -E1000_ERR_NVM; - s32 i = 0; hsfsts.regval = er16flash(ICH_FLASH_HSFSTS); @@ -2140,6 +2139,8 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) ew16flash(ICH_FLASH_HSFSTS, hsfsts.regval); ret_val = 0; } else { + s32 i = 0; + /* * Otherwise poll for sometime so the current * cycle has a chance to end before giving up. diff --git a/drivers/net/e1000e/lib.c b/drivers/net/e1000e/lib.c index 68aa1749bf6..96921de5df2 100644 --- a/drivers/net/e1000e/lib.c +++ b/drivers/net/e1000e/lib.c @@ -1978,15 +1978,15 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw) { struct e1000_nvm_info *nvm = &hw->nvm; u32 eecd = er32(EECD); - u16 timeout = 0; u8 spi_stat_reg; if (nvm->type == e1000_nvm_eeprom_spi) { + u16 timeout = NVM_MAX_RETRY_SPI; + /* Clear SK and CS */ eecd &= ~(E1000_EECD_CS | E1000_EECD_SK); ew32(EECD, eecd); udelay(1); - timeout = NVM_MAX_RETRY_SPI; /* * Read "Status Register" repeatedly until the LSB is cleared. diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 1c18f26b081..5b916b01805 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -900,8 +900,6 @@ next_desc: adapter->total_rx_bytes += total_rx_bytes; adapter->total_rx_packets += total_rx_packets; - netdev->stats.rx_bytes += total_rx_bytes; - netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -1057,8 +1055,6 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter) } adapter->total_tx_bytes += total_tx_bytes; adapter->total_tx_packets += total_tx_packets; - netdev->stats.tx_bytes += total_tx_bytes; - netdev->stats.tx_packets += total_tx_packets; return count < tx_ring->count; } @@ -1245,8 +1241,6 @@ next_desc: adapter->total_rx_bytes += total_rx_bytes; adapter->total_rx_packets += total_rx_packets; - netdev->stats.rx_bytes += total_rx_bytes; - netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -1426,8 +1420,6 @@ next_desc: adapter->total_rx_bytes += total_rx_bytes; adapter->total_rx_packets += total_rx_packets; - netdev->stats.rx_bytes += total_rx_bytes; - netdev->stats.rx_packets += total_rx_packets; return cleaned; } @@ -2728,7 +2720,6 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; u32 rctl, rfctl; - u32 psrctl = 0; u32 pages = 0; /* Workaround Si errata on 82579 - configure jumbo frame flow */ @@ -2827,6 +2818,8 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) adapter->rx_ps_pages = 0; if (adapter->rx_ps_pages) { + u32 psrctl = 0; + /* Configure extra packet-split registers */ rfctl = er32(RFCTL); rfctl |= E1000_RFCTL_EXTEN; @@ -3028,7 +3021,6 @@ static void e1000_set_multi(struct net_device *netdev) struct netdev_hw_addr *ha; u8 *mta_list; u32 rctl; - int i; /* Check for Promiscuous and All Multicast modes */ @@ -3051,12 +3043,13 @@ static void e1000_set_multi(struct net_device *netdev) ew32(RCTL, rctl); if (!netdev_mc_empty(netdev)) { + int i = 0; + mta_list = kmalloc(netdev_mc_count(netdev) * 6, GFP_ATOMIC); if (!mta_list) return; /* prepare a packed array of only addresses. */ - i = 0; netdev_for_each_mc_addr(ha, netdev) memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN); @@ -3338,6 +3331,8 @@ int e1000e_up(struct e1000_adapter *adapter) return 0; } +static void e1000e_update_stats(struct e1000_adapter *adapter); + void e1000e_down(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -3372,6 +3367,11 @@ void e1000e_down(struct e1000_adapter *adapter) del_timer_sync(&adapter->phy_info_timer); netif_carrier_off(netdev); + + spin_lock(&adapter->stats64_lock); + e1000e_update_stats(adapter); + spin_unlock(&adapter->stats64_lock); + adapter->link_speed = 0; adapter->link_duplex = 0; @@ -3413,6 +3413,8 @@ static int __devinit e1000_sw_init(struct e1000_adapter *adapter) adapter->max_frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN; adapter->min_frame_size = ETH_ZLEN + ETH_FCS_LEN; + spin_lock_init(&adapter->stats64_lock); + e1000e_set_interrupt_capability(adapter); if (e1000_alloc_queues(adapter)) @@ -3886,7 +3888,7 @@ release: * e1000e_update_stats - Update the board statistics counters * @adapter: board private structure **/ -void e1000e_update_stats(struct e1000_adapter *adapter) +static void e1000e_update_stats(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct e1000_hw *hw = &adapter->hw; @@ -3998,10 +4000,11 @@ static void e1000_phy_read_status(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct e1000_phy_regs *phy = &adapter->phy_regs; - int ret_val; if ((er32(STATUS) & E1000_STATUS_LU) && (adapter->hw.phy.media_type == e1000_media_type_copper)) { + int ret_val; + ret_val = e1e_rphy(hw, PHY_CONTROL, &phy->bmcr); ret_val |= e1e_rphy(hw, PHY_STATUS, &phy->bmsr); ret_val |= e1e_rphy(hw, PHY_AUTONEG_ADV, &phy->advertise); @@ -4147,7 +4150,6 @@ static void e1000_watchdog_task(struct work_struct *work) struct e1000_ring *tx_ring = adapter->tx_ring; struct e1000_hw *hw = &adapter->hw; u32 link, tctl; - int tx_pending = 0; link = e1000e_has_link(adapter); if ((netif_carrier_ok(netdev)) && link) { @@ -4285,7 +4287,9 @@ static void e1000_watchdog_task(struct work_struct *work) } link_up: + spin_lock(&adapter->stats64_lock); e1000e_update_stats(adapter); + spin_unlock(&adapter->stats64_lock); mac->tx_packet_delta = adapter->stats.tpt - adapter->tpt_old; adapter->tpt_old = adapter->stats.tpt; @@ -4299,21 +4303,18 @@ link_up: e1000e_update_adaptive(&adapter->hw); - if (!netif_carrier_ok(netdev)) { - tx_pending = (e1000_desc_unused(tx_ring) + 1 < - tx_ring->count); - if (tx_pending) { - /* - * We've lost link, so the controller stops DMA, - * but we've got queued Tx work that's never going - * to get done, so reset controller to flush Tx. - * (Do the reset outside of interrupt context). - */ - adapter->tx_timeout_count++; - schedule_work(&adapter->reset_task); - /* return immediately since reset is imminent */ - return; - } + if (!netif_carrier_ok(netdev) && + (e1000_desc_unused(tx_ring) + 1 < tx_ring->count)) { + /* + * We've lost link, so the controller stops DMA, + * but we've got queued Tx work that's never going + * to get done, so reset controller to flush Tx. + * (Do the reset outside of interrupt context). + */ + adapter->tx_timeout_count++; + schedule_work(&adapter->reset_task); + /* return immediately since reset is imminent */ + return; } /* Simple mode for Interrupt Throttle Rate (ITR) */ @@ -4384,13 +4385,13 @@ static int e1000_tso(struct e1000_adapter *adapter, u32 cmd_length = 0; u16 ipcse = 0, tucse, mss; u8 ipcss, ipcso, tucss, tucso, hdr_len; - int err; if (!skb_is_gso(skb)) return 0; if (skb_header_cloned(skb)) { - err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + int err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); + if (err) return err; } @@ -4897,16 +4898,55 @@ static void e1000_reset_task(struct work_struct *work) } /** - * e1000_get_stats - Get System Network Statistics + * e1000_get_stats64 - Get System Network Statistics * @netdev: network interface device structure + * @stats: rtnl_link_stats64 pointer * * Returns the address of the device statistics structure. - * The statistics are actually updated from the timer callback. **/ -static struct net_device_stats *e1000_get_stats(struct net_device *netdev) +struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) { - /* only return the current stats */ - return &netdev->stats; + struct e1000_adapter *adapter = netdev_priv(netdev); + + memset(stats, 0, sizeof(struct rtnl_link_stats64)); + spin_lock(&adapter->stats64_lock); + e1000e_update_stats(adapter); + /* Fill out the OS statistics structure */ + stats->rx_bytes = adapter->stats.gorc; + stats->rx_packets = adapter->stats.gprc; + stats->tx_bytes = adapter->stats.gotc; + stats->tx_packets = adapter->stats.gptc; + stats->multicast = adapter->stats.mprc; + stats->collisions = adapter->stats.colc; + + /* Rx Errors */ + + /* + * RLEC on some newer hardware can be incorrect so build + * our own version based on RUC and ROC + */ + stats->rx_errors = adapter->stats.rxerrc + + adapter->stats.crcerrs + adapter->stats.algnerrc + + adapter->stats.ruc + adapter->stats.roc + + adapter->stats.cexterr; + stats->rx_length_errors = adapter->stats.ruc + + adapter->stats.roc; + stats->rx_crc_errors = adapter->stats.crcerrs; + stats->rx_frame_errors = adapter->stats.algnerrc; + stats->rx_missed_errors = adapter->stats.mpc; + + /* Tx Errors */ + stats->tx_errors = adapter->stats.ecol + + adapter->stats.latecol; + stats->tx_aborted_errors = adapter->stats.ecol; + stats->tx_window_errors = adapter->stats.latecol; + stats->tx_carrier_errors = adapter->stats.tncrs; + + /* Tx Dropped needs to be maintained elsewhere */ + + spin_unlock(&adapter->stats64_lock); + return stats; } /** @@ -5476,9 +5516,10 @@ static irqreturn_t e1000_intr_msix(int irq, void *data) { struct net_device *netdev = data; struct e1000_adapter *adapter = netdev_priv(netdev); - int vector, msix_irq; if (adapter->msix_entries) { + int vector, msix_irq; + vector = 0; msix_irq = adapter->msix_entries[vector].vector; disable_irq(msix_irq); @@ -5675,7 +5716,7 @@ static const struct net_device_ops e1000e_netdev_ops = { .ndo_open = e1000_open, .ndo_stop = e1000_close, .ndo_start_xmit = e1000_xmit_frame, - .ndo_get_stats = e1000_get_stats, + .ndo_get_stats64 = e1000e_get_stats64, .ndo_set_multicast_list = e1000_set_multi, .ndo_set_mac_address = e1000_set_mac, .ndo_change_mtu = e1000_change_mtu, diff --git a/drivers/net/e1000e/phy.c b/drivers/net/e1000e/phy.c index 6bea051b134..6ae31fcfb62 100644 --- a/drivers/net/e1000e/phy.c +++ b/drivers/net/e1000e/phy.c @@ -2409,9 +2409,7 @@ static u32 e1000_get_phy_addr_for_bm_page(u32 page, u32 reg) s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) { s32 ret_val; - u32 page_select = 0; u32 page = offset >> IGP_PAGE_SHIFT; - u32 page_shift = 0; ret_val = hw->phy.ops.acquire(hw); if (ret_val) @@ -2427,6 +2425,8 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); if (offset > MAX_PHY_MULTI_PAGE_REG) { + u32 page_shift, page_select; + /* * Page select is register 31 for phy address 1 and 22 for * phy address 2 and 3. Page select is shifted only for @@ -2468,9 +2468,7 @@ out: s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) { s32 ret_val; - u32 page_select = 0; u32 page = offset >> IGP_PAGE_SHIFT; - u32 page_shift = 0; ret_val = hw->phy.ops.acquire(hw); if (ret_val) @@ -2486,6 +2484,8 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) hw->phy.addr = e1000_get_phy_addr_for_bm_page(page, offset); if (offset > MAX_PHY_MULTI_PAGE_REG) { + u32 page_shift, page_select; + /* * Page select is register 31 for phy address 1 and 22 for * phy address 2 and 3. Page select is shifted only for diff --git a/drivers/net/enic/enic.h b/drivers/net/enic/enic.h index a937f49d9db..ca3be4f1555 100644 --- a/drivers/net/enic/enic.h +++ b/drivers/net/enic/enic.h @@ -32,8 +32,8 @@ #define DRV_NAME "enic" #define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver" -#define DRV_VERSION "1.4.1.10" -#define DRV_COPYRIGHT "Copyright 2008-2010 Cisco Systems, Inc" +#define DRV_VERSION "2.1.1.2" +#define DRV_COPYRIGHT "Copyright 2008-2011 Cisco Systems, Inc" #define ENIC_BARS_MAX 6 @@ -49,7 +49,7 @@ struct enic_msix_entry { void *devid; }; -#define ENIC_SET_APPLIED (1 << 0) +#define ENIC_PORT_REQUEST_APPLIED (1 << 0) #define ENIC_SET_REQUEST (1 << 1) #define ENIC_SET_NAME (1 << 2) #define ENIC_SET_INSTANCE (1 << 3) diff --git a/drivers/net/enic/enic_main.c b/drivers/net/enic/enic_main.c index a0af48c51fb..89664c67097 100644 --- a/drivers/net/enic/enic_main.c +++ b/drivers/net/enic/enic_main.c @@ -1318,18 +1318,20 @@ static int enic_set_port_profile(struct enic *enic, u8 *mac) vic_provinfo_free(vp); if (err) return err; - - enic->pp.set |= ENIC_SET_APPLIED; break; case PORT_REQUEST_DISASSOCIATE: - enic->pp.set &= ~ENIC_SET_APPLIED; break; default: return -EINVAL; } + /* Set flag to indicate that the port assoc/disassoc + * request has been sent out to fw + */ + enic->pp.set |= ENIC_PORT_REQUEST_APPLIED; + return 0; } @@ -1411,7 +1413,7 @@ static int enic_get_vf_port(struct net_device *netdev, int vf, int err, error, done; u16 response = PORT_PROFILE_RESPONSE_SUCCESS; - if (!(enic->pp.set & ENIC_SET_APPLIED)) + if (!(enic->pp.set & ENIC_PORT_REQUEST_APPLIED)) return -ENODATA; err = enic_dev_init_done(enic, &done, &error); diff --git a/drivers/net/igb/e1000_82575.c b/drivers/net/igb/e1000_82575.c index 0a2368fa6bc..c1552b6f4a6 100644 --- a/drivers/net/igb/e1000_82575.c +++ b/drivers/net/igb/e1000_82575.c @@ -129,6 +129,7 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw) break; case E1000_DEV_ID_82580_COPPER: case E1000_DEV_ID_82580_FIBER: + case E1000_DEV_ID_82580_QUAD_FIBER: case E1000_DEV_ID_82580_SERDES: case E1000_DEV_ID_82580_SGMII: case E1000_DEV_ID_82580_COPPER_DUAL: diff --git a/drivers/net/igb/e1000_hw.h b/drivers/net/igb/e1000_hw.h index e2638afb8cd..281324e8598 100644 --- a/drivers/net/igb/e1000_hw.h +++ b/drivers/net/igb/e1000_hw.h @@ -54,6 +54,7 @@ struct e1000_hw; #define E1000_DEV_ID_82580_SERDES 0x1510 #define E1000_DEV_ID_82580_SGMII 0x1511 #define E1000_DEV_ID_82580_COPPER_DUAL 0x1516 +#define E1000_DEV_ID_82580_QUAD_FIBER 0x1527 #define E1000_DEV_ID_DH89XXCC_SGMII 0x0438 #define E1000_DEV_ID_DH89XXCC_SERDES 0x043A #define E1000_DEV_ID_DH89XXCC_BACKPLANE 0x043C diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 58c665b7513..200cc320967 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -68,6 +68,7 @@ static DEFINE_PCI_DEVICE_TABLE(igb_pci_tbl) = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_I350_SGMII), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_FIBER), board_82575 }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_QUAD_FIBER), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SERDES), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_SGMII), board_82575 }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_82580_COPPER_DUAL), board_82575 }, diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index c7a6c446697..9f6d670748d 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -592,8 +592,8 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ppp_release(NULL, file); err = 0; } else - printk(KERN_DEBUG "PPPIOCDETACH file->f_count=%ld\n", - atomic_long_read(&file->f_count)); + pr_warn("PPPIOCDETACH file->f_count=%ld\n", + atomic_long_read(&file->f_count)); mutex_unlock(&ppp_mutex); return err; } @@ -630,7 +630,7 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (pf->kind != INTERFACE) { /* can't happen */ - printk(KERN_ERR "PPP: not interface or channel??\n"); + pr_err("PPP: not interface or channel??\n"); return -EINVAL; } @@ -704,7 +704,8 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } vj = slhc_init(val2+1, val+1); if (!vj) { - printk(KERN_ERR "PPP: no memory (VJ compressor)\n"); + netdev_err(ppp->dev, + "PPP: no memory (VJ compressor)\n"); err = -ENOMEM; break; } @@ -898,17 +899,17 @@ static int __init ppp_init(void) { int err; - printk(KERN_INFO "PPP generic driver version " PPP_VERSION "\n"); + pr_info("PPP generic driver version " PPP_VERSION "\n"); err = register_pernet_device(&ppp_net_ops); if (err) { - printk(KERN_ERR "failed to register PPP pernet device (%d)\n", err); + pr_err("failed to register PPP pernet device (%d)\n", err); goto out; } err = register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); if (err) { - printk(KERN_ERR "failed to register PPP device (%d)\n", err); + pr_err("failed to register PPP device (%d)\n", err); goto out_net; } @@ -1078,7 +1079,7 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb) new_skb = alloc_skb(new_skb_size, GFP_ATOMIC); if (!new_skb) { if (net_ratelimit()) - printk(KERN_ERR "PPP: no memory (comp pkt)\n"); + netdev_err(ppp->dev, "PPP: no memory (comp pkt)\n"); return NULL; } if (ppp->dev->hard_header_len > PPP_HDRLEN) @@ -1108,7 +1109,7 @@ pad_compress_skb(struct ppp *ppp, struct sk_buff *skb) * the same number. */ if (net_ratelimit()) - printk(KERN_ERR "ppp: compressor dropped pkt\n"); + netdev_err(ppp->dev, "ppp: compressor dropped pkt\n"); kfree_skb(skb); kfree_skb(new_skb); new_skb = NULL; @@ -1138,7 +1139,9 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) if (ppp->pass_filter && sk_run_filter(skb, ppp->pass_filter) == 0) { if (ppp->debug & 1) - printk(KERN_DEBUG "PPP: outbound frame not passed\n"); + netdev_printk(KERN_DEBUG, ppp->dev, + "PPP: outbound frame " + "not passed\n"); kfree_skb(skb); return; } @@ -1164,7 +1167,7 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) new_skb = alloc_skb(skb->len + ppp->dev->hard_header_len - 2, GFP_ATOMIC); if (!new_skb) { - printk(KERN_ERR "PPP: no memory (VJ comp pkt)\n"); + netdev_err(ppp->dev, "PPP: no memory (VJ comp pkt)\n"); goto drop; } skb_reserve(new_skb, ppp->dev->hard_header_len - 2); @@ -1202,7 +1205,9 @@ ppp_send_frame(struct ppp *ppp, struct sk_buff *skb) proto != PPP_LCP && proto != PPP_CCP) { if (!(ppp->flags & SC_CCP_UP) && (ppp->flags & SC_MUST_COMP)) { if (net_ratelimit()) - printk(KERN_ERR "ppp: compression required but down - pkt dropped.\n"); + netdev_err(ppp->dev, + "ppp: compression required but " + "down - pkt dropped.\n"); goto drop; } skb = pad_compress_skb(ppp, skb); @@ -1505,7 +1510,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) noskb: spin_unlock_bh(&pch->downl); if (ppp->debug & 1) - printk(KERN_ERR "PPP: no memory (fragment)\n"); + netdev_err(ppp->dev, "PPP: no memory (fragment)\n"); ++ppp->dev->stats.tx_errors; ++ppp->nxseq; return 1; /* abandon the frame */ @@ -1686,7 +1691,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) /* copy to a new sk_buff with more tailroom */ ns = dev_alloc_skb(skb->len + 128); if (!ns) { - printk(KERN_ERR"PPP: no memory (VJ decomp)\n"); + netdev_err(ppp->dev, "PPP: no memory " + "(VJ decomp)\n"); goto err; } skb_reserve(ns, 2); @@ -1699,7 +1705,8 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2); if (len <= 0) { - printk(KERN_DEBUG "PPP: VJ decompression error\n"); + netdev_printk(KERN_DEBUG, ppp->dev, + "PPP: VJ decompression error\n"); goto err; } len += 2; @@ -1721,7 +1728,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) goto err; if (slhc_remember(ppp->vj, skb->data + 2, skb->len - 2) <= 0) { - printk(KERN_ERR "PPP: VJ uncompressed error\n"); + netdev_err(ppp->dev, "PPP: VJ uncompressed error\n"); goto err; } proto = PPP_IP; @@ -1762,8 +1769,9 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) if (ppp->pass_filter && sk_run_filter(skb, ppp->pass_filter) == 0) { if (ppp->debug & 1) - printk(KERN_DEBUG "PPP: inbound frame " - "not passed\n"); + netdev_printk(KERN_DEBUG, ppp->dev, + "PPP: inbound frame " + "not passed\n"); kfree_skb(skb); return; } @@ -1821,7 +1829,8 @@ ppp_decompress_frame(struct ppp *ppp, struct sk_buff *skb) ns = dev_alloc_skb(obuff_size); if (!ns) { - printk(KERN_ERR "ppp_decompress_frame: no memory\n"); + netdev_err(ppp->dev, "ppp_decompress_frame: " + "no memory\n"); goto err; } /* the decompressor still expects the A/C bytes in the hdr */ @@ -1989,7 +1998,7 @@ ppp_mp_reconstruct(struct ppp *ppp) u32 seq = ppp->nextseq; u32 minseq = ppp->minseq; struct sk_buff_head *list = &ppp->mrq; - struct sk_buff *p, *next; + struct sk_buff *p, *tmp; struct sk_buff *head, *tail; struct sk_buff *skb = NULL; int lost = 0, len = 0; @@ -1998,13 +2007,15 @@ ppp_mp_reconstruct(struct ppp *ppp) return NULL; head = list->next; tail = NULL; - for (p = head; p != (struct sk_buff *) list; p = next) { - next = p->next; + skb_queue_walk_safe(list, p, tmp) { + again: if (seq_before(PPP_MP_CB(p)->sequence, seq)) { /* this can't happen, anyway ignore the skb */ - printk(KERN_ERR "ppp_mp_reconstruct bad seq %u < %u\n", - PPP_MP_CB(p)->sequence, seq); - head = next; + netdev_err(ppp->dev, "ppp_mp_reconstruct bad " + "seq %u < %u\n", + PPP_MP_CB(p)->sequence, seq); + __skb_unlink(p, list); + kfree_skb(p); continue; } if (PPP_MP_CB(p)->sequence != seq) { @@ -2016,8 +2027,7 @@ ppp_mp_reconstruct(struct ppp *ppp) lost = 1; seq = seq_before(minseq, PPP_MP_CB(p)->sequence)? minseq + 1: PPP_MP_CB(p)->sequence; - next = p; - continue; + goto again; } /* @@ -2042,17 +2052,9 @@ ppp_mp_reconstruct(struct ppp *ppp) (PPP_MP_CB(head)->BEbits & B)) { if (len > ppp->mrru + 2) { ++ppp->dev->stats.rx_length_errors; - printk(KERN_DEBUG "PPP: reconstructed packet" - " is too long (%d)\n", len); - } else if (p == head) { - /* fragment is complete packet - reuse skb */ - tail = p; - skb = skb_get(p); - break; - } else if ((skb = dev_alloc_skb(len)) == NULL) { - ++ppp->dev->stats.rx_missed_errors; - printk(KERN_DEBUG "PPP: no memory for " - "reconstructed packet"); + netdev_printk(KERN_DEBUG, ppp->dev, + "PPP: reconstructed packet" + " is too long (%d)\n", len); } else { tail = p; break; @@ -2065,9 +2067,17 @@ ppp_mp_reconstruct(struct ppp *ppp) * and we haven't found a complete valid packet yet, * we can discard up to and including this fragment. */ - if (PPP_MP_CB(p)->BEbits & E) - head = next; + if (PPP_MP_CB(p)->BEbits & E) { + struct sk_buff *tmp2; + skb_queue_reverse_walk_from_safe(list, p, tmp2) { + __skb_unlink(p, list); + kfree_skb(p); + } + head = skb_peek(list); + if (!head) + break; + } ++seq; } @@ -2077,26 +2087,37 @@ ppp_mp_reconstruct(struct ppp *ppp) signal a receive error. */ if (PPP_MP_CB(head)->sequence != ppp->nextseq) { if (ppp->debug & 1) - printk(KERN_DEBUG " missed pkts %u..%u\n", - ppp->nextseq, - PPP_MP_CB(head)->sequence-1); + netdev_printk(KERN_DEBUG, ppp->dev, + " missed pkts %u..%u\n", + ppp->nextseq, + PPP_MP_CB(head)->sequence-1); ++ppp->dev->stats.rx_dropped; ppp_receive_error(ppp); } - if (head != tail) - /* copy to a single skb */ - for (p = head; p != tail->next; p = p->next) - skb_copy_bits(p, 0, skb_put(skb, p->len), p->len); - ppp->nextseq = PPP_MP_CB(tail)->sequence + 1; - head = tail->next; - } + skb = head; + if (head != tail) { + struct sk_buff **fragpp = &skb_shinfo(skb)->frag_list; + p = skb_queue_next(list, head); + __skb_unlink(skb, list); + skb_queue_walk_from_safe(list, p, tmp) { + __skb_unlink(p, list); + *fragpp = p; + p->next = NULL; + fragpp = &p->next; + + skb->len += p->len; + skb->data_len += p->len; + skb->truesize += p->len; + + if (p == tail) + break; + } + } else { + __skb_unlink(skb, list); + } - /* Discard all the skbuffs that we have copied the data out of - or that we can't use. */ - while ((p = list->next) != head) { - __skb_unlink(p, list); - kfree_skb(p); + ppp->nextseq = PPP_MP_CB(tail)->sequence + 1; } return skb; @@ -2617,8 +2638,8 @@ ppp_create_interface(struct net *net, int unit, int *retp) ret = register_netdev(dev); if (ret != 0) { unit_put(&pn->units_idr, unit); - printk(KERN_ERR "PPP: couldn't register device %s (%d)\n", - dev->name, ret); + netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n", + dev->name, ret); goto out2; } @@ -2690,9 +2711,9 @@ static void ppp_destroy_interface(struct ppp *ppp) if (!ppp->file.dead || ppp->n_channels) { /* "can't happen" */ - printk(KERN_ERR "ppp: destroying ppp struct %p but dead=%d " - "n_channels=%d !\n", ppp, ppp->file.dead, - ppp->n_channels); + netdev_err(ppp->dev, "ppp: destroying ppp struct %p " + "but dead=%d n_channels=%d !\n", + ppp, ppp->file.dead, ppp->n_channels); return; } @@ -2834,8 +2855,7 @@ static void ppp_destroy_channel(struct channel *pch) if (!pch->file.dead) { /* "can't happen" */ - printk(KERN_ERR "ppp: destroying undead channel %p !\n", - pch); + pr_err("ppp: destroying undead channel %p !\n", pch); return; } skb_queue_purge(&pch->file.xq); @@ -2847,7 +2867,7 @@ static void __exit ppp_cleanup(void) { /* should never happen */ if (atomic_read(&ppp_unit_count) || atomic_read(&channel_count)) - printk(KERN_ERR "PPP: removing module but units remain!\n"); + pr_err("PPP: removing module but units remain!\n"); unregister_chrdev(PPP_MAJOR, "ppp"); device_destroy(ppp_class, MKDEV(PPP_MAJOR, 0)); class_destroy(ppp_class); @@ -2865,7 +2885,7 @@ static int __unit_alloc(struct idr *p, void *ptr, int n) again: if (!idr_pre_get(p, GFP_KERNEL)) { - printk(KERN_ERR "PPP: No free memory for idr\n"); + pr_err("PPP: No free memory for idr\n"); return -ENOMEM; } diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 09cac704fdd..0d6fec6b7d9 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -2923,6 +2923,7 @@ static u16 wol_calc_crc(int size, u8 *pattern, u8 *mask_pattern) static int velocity_set_wol(struct velocity_info *vptr) { struct mac_regs __iomem *regs = vptr->mac_regs; + enum speed_opt spd_dpx = vptr->options.spd_dpx; static u8 buf[256]; int i; @@ -2968,6 +2969,12 @@ static int velocity_set_wol(struct velocity_info *vptr) writew(0x0FFF, ®s->WOLSRClr); + if (spd_dpx == SPD_DPX_1000_FULL) + goto mac_done; + + if (spd_dpx != SPD_DPX_AUTO) + goto advertise_done; + if (vptr->mii_status & VELOCITY_AUTONEG_ENABLE) { if (PHYID_GET_PHY_ID(vptr->phy_id) == PHYID_CICADA_CS8201) MII_REG_BITS_ON(AUXCR_MDPPS, MII_NCONFIG, vptr->mac_regs); @@ -2978,6 +2985,7 @@ static int velocity_set_wol(struct velocity_info *vptr) if (vptr->mii_status & VELOCITY_SPEED_1000) MII_REG_BITS_ON(BMCR_ANRESTART, MII_BMCR, vptr->mac_regs); +advertise_done: BYTE_REG_BITS_ON(CHIPGCR_FCMODE, ®s->CHIPGCR); { @@ -2987,6 +2995,7 @@ static int velocity_set_wol(struct velocity_info *vptr) writeb(GCR, ®s->CHIPGCR); } +mac_done: BYTE_REG_BITS_OFF(ISR_PWEI, ®s->ISR); /* Turn on SWPTAG just before entering power mode */ BYTE_REG_BITS_ON(STICKHW_SWPTAG, ®s->STICKHW); diff --git a/drivers/net/via-velocity.h b/drivers/net/via-velocity.h index aa2e69b9ff6..d7227539484 100644 --- a/drivers/net/via-velocity.h +++ b/drivers/net/via-velocity.h @@ -361,7 +361,7 @@ enum velocity_owner { #define MAC_REG_CHIPGSR 0x9C #define MAC_REG_TESTCFG 0x9D #define MAC_REG_DEBUG 0x9E -#define MAC_REG_CHIPGCR 0x9F +#define MAC_REG_CHIPGCR 0x9F /* Chip Operation and Diagnostic Control */ #define MAC_REG_WOLCR0_SET 0xA0 #define MAC_REG_WOLCR1_SET 0xA1 #define MAC_REG_PWCFG_SET 0xA2 @@ -848,10 +848,10 @@ enum velocity_owner { * Bits in CHIPGCR register */ -#define CHIPGCR_FCGMII 0x80 /* enable GMII mode */ -#define CHIPGCR_FCFDX 0x40 +#define CHIPGCR_FCGMII 0x80 /* force GMII (else MII only) */ +#define CHIPGCR_FCFDX 0x40 /* force full duplex */ #define CHIPGCR_FCRESV 0x20 -#define CHIPGCR_FCMODE 0x10 +#define CHIPGCR_FCMODE 0x10 /* enable MAC forced mode */ #define CHIPGCR_LPSOPT 0x08 #define CHIPGCR_TM1US 0x04 #define CHIPGCR_TM0US 0x02 diff --git a/drivers/net/vxge/vxge-config.c b/drivers/net/vxge/vxge-config.c index 01c05f53e2f..77097e383cf 100644 --- a/drivers/net/vxge/vxge-config.c +++ b/drivers/net/vxge/vxge-config.c @@ -387,8 +387,8 @@ vxge_hw_vpath_eprom_img_ver_get(struct __vxge_hw_device *hldev, data1 = steer_ctrl = 0; status = vxge_hw_vpath_fw_api(vpath, - VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_FW_MEMO, VXGE_HW_FW_API_GET_EPROM_REV, + VXGE_HW_RTS_ACCESS_STEER_CTRL_DATA_STRUCT_SEL_FW_MEMO, 0, &data0, &data1, &steer_ctrl); if (status != VXGE_HW_OK) break; @@ -2868,6 +2868,8 @@ __vxge_hw_ring_create(struct __vxge_hw_vpath_handle *vp, ring->rxd_init = attr->rxd_init; ring->rxd_term = attr->rxd_term; ring->buffer_mode = config->buffer_mode; + ring->tim_rti_cfg1_saved = vp->vpath->tim_rti_cfg1_saved; + ring->tim_rti_cfg3_saved = vp->vpath->tim_rti_cfg3_saved; ring->rxds_limit = config->rxds_limit; ring->rxd_size = vxge_hw_ring_rxd_size_get(config->buffer_mode); @@ -3511,6 +3513,8 @@ __vxge_hw_fifo_create(struct __vxge_hw_vpath_handle *vp, /* apply "interrupts per txdl" attribute */ fifo->interrupt_type = VXGE_HW_FIFO_TXD_INT_TYPE_UTILZ; + fifo->tim_tti_cfg1_saved = vpath->tim_tti_cfg1_saved; + fifo->tim_tti_cfg3_saved = vpath->tim_tti_cfg3_saved; if (fifo->config->intr) fifo->interrupt_type = VXGE_HW_FIFO_TXD_INT_TYPE_PER_LIST; @@ -4377,6 +4381,8 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) } writeq(val64, &vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); + vpath->tim_tti_cfg1_saved = val64; + val64 = readq(&vp_reg->tim_cfg2_int_num[VXGE_HW_VPATH_INTR_TX]); if (config->tti.uec_a != VXGE_HW_USE_FLASH_DEFAULT) { @@ -4433,6 +4439,7 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) } writeq(val64, &vp_reg->tim_cfg3_int_num[VXGE_HW_VPATH_INTR_TX]); + vpath->tim_tti_cfg3_saved = val64; } if (config->ring.enable == VXGE_HW_RING_ENABLE) { @@ -4481,6 +4488,8 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) } writeq(val64, &vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_RX]); + vpath->tim_rti_cfg1_saved = val64; + val64 = readq(&vp_reg->tim_cfg2_int_num[VXGE_HW_VPATH_INTR_RX]); if (config->rti.uec_a != VXGE_HW_USE_FLASH_DEFAULT) { @@ -4537,6 +4546,7 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) } writeq(val64, &vp_reg->tim_cfg3_int_num[VXGE_HW_VPATH_INTR_RX]); + vpath->tim_rti_cfg3_saved = val64; } val64 = 0; @@ -4555,26 +4565,6 @@ __vxge_hw_vpath_tim_configure(struct __vxge_hw_device *hldev, u32 vp_id) return status; } -void vxge_hw_vpath_tti_ci_set(struct __vxge_hw_device *hldev, u32 vp_id) -{ - struct __vxge_hw_virtualpath *vpath; - struct vxge_hw_vpath_reg __iomem *vp_reg; - struct vxge_hw_vp_config *config; - u64 val64; - - vpath = &hldev->virtual_paths[vp_id]; - vp_reg = vpath->vp_reg; - config = vpath->vp_config; - - if (config->fifo.enable == VXGE_HW_FIFO_ENABLE && - config->tti.timer_ci_en != VXGE_HW_TIM_TIMER_CI_ENABLE) { - config->tti.timer_ci_en = VXGE_HW_TIM_TIMER_CI_ENABLE; - val64 = readq(&vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); - val64 |= VXGE_HW_TIM_CFG1_INT_NUM_TIMER_CI; - writeq(val64, &vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); - } -} - /* * __vxge_hw_vpath_initialize * This routine is the final phase of init which initializes the diff --git a/drivers/net/vxge/vxge-config.h b/drivers/net/vxge/vxge-config.h index e249e288d16..3c53aa732c9 100644 --- a/drivers/net/vxge/vxge-config.h +++ b/drivers/net/vxge/vxge-config.h @@ -682,6 +682,10 @@ struct __vxge_hw_virtualpath { u32 vsport_number; u32 max_kdfc_db; u32 max_nofl_db; + u64 tim_tti_cfg1_saved; + u64 tim_tti_cfg3_saved; + u64 tim_rti_cfg1_saved; + u64 tim_rti_cfg3_saved; struct __vxge_hw_ring *____cacheline_aligned ringh; struct __vxge_hw_fifo *____cacheline_aligned fifoh; @@ -921,6 +925,9 @@ struct __vxge_hw_ring { u32 doorbell_cnt; u32 total_db_cnt; u64 rxds_limit; + u32 rtimer; + u64 tim_rti_cfg1_saved; + u64 tim_rti_cfg3_saved; enum vxge_hw_status (*callback)( struct __vxge_hw_ring *ringh, @@ -1000,6 +1007,9 @@ struct __vxge_hw_fifo { u32 per_txdl_space; u32 vp_id; u32 tx_intr_num; + u32 rtimer; + u64 tim_tti_cfg1_saved; + u64 tim_tti_cfg3_saved; enum vxge_hw_status (*callback)( struct __vxge_hw_fifo *fifo_handle, diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c index c81a6512c68..e40f619b62b 100644 --- a/drivers/net/vxge/vxge-main.c +++ b/drivers/net/vxge/vxge-main.c @@ -371,9 +371,6 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr, struct vxge_hw_ring_rxd_info ext_info; vxge_debug_entryexit(VXGE_TRACE, "%s: %s:%d", ring->ndev->name, __func__, __LINE__); - ring->pkts_processed = 0; - - vxge_hw_ring_replenish(ringh); do { prefetch((char *)dtr + L1_CACHE_BYTES); @@ -1588,6 +1585,36 @@ static int vxge_reset_vpath(struct vxgedev *vdev, int vp_id) return ret; } +/* Configure CI */ +static void vxge_config_ci_for_tti_rti(struct vxgedev *vdev) +{ + int i = 0; + + /* Enable CI for RTI */ + if (vdev->config.intr_type == MSI_X) { + for (i = 0; i < vdev->no_of_vpath; i++) { + struct __vxge_hw_ring *hw_ring; + + hw_ring = vdev->vpaths[i].ring.handle; + vxge_hw_vpath_dynamic_rti_ci_set(hw_ring); + } + } + + /* Enable CI for TTI */ + for (i = 0; i < vdev->no_of_vpath; i++) { + struct __vxge_hw_fifo *hw_fifo = vdev->vpaths[i].fifo.handle; + vxge_hw_vpath_tti_ci_set(hw_fifo); + /* + * For Inta (with or without napi), Set CI ON for only one + * vpath. (Have only one free running timer). + */ + if ((vdev->config.intr_type == INTA) && (i == 0)) + break; + } + + return; +} + static int do_vxge_reset(struct vxgedev *vdev, int event) { enum vxge_hw_status status; @@ -1753,6 +1780,9 @@ static int do_vxge_reset(struct vxgedev *vdev, int event) netif_tx_wake_all_queues(vdev->ndev); } + /* configure CI */ + vxge_config_ci_for_tti_rti(vdev); + out: vxge_debug_entryexit(VXGE_TRACE, "%s:%d Exiting...", __func__, __LINE__); @@ -1793,22 +1823,29 @@ static void vxge_reset(struct work_struct *work) */ static int vxge_poll_msix(struct napi_struct *napi, int budget) { - struct vxge_ring *ring = - container_of(napi, struct vxge_ring, napi); + struct vxge_ring *ring = container_of(napi, struct vxge_ring, napi); + int pkts_processed; int budget_org = budget; - ring->budget = budget; + ring->budget = budget; + ring->pkts_processed = 0; vxge_hw_vpath_poll_rx(ring->handle); + pkts_processed = ring->pkts_processed; if (ring->pkts_processed < budget_org) { napi_complete(napi); + /* Re enable the Rx interrupts for the vpath */ vxge_hw_channel_msix_unmask( (struct __vxge_hw_channel *)ring->handle, ring->rx_vector_no); + mmiowb(); } - return ring->pkts_processed; + /* We are copying and returning the local variable, in case if after + * clearing the msix interrupt above, if the interrupt fires right + * away which can preempt this NAPI thread */ + return pkts_processed; } static int vxge_poll_inta(struct napi_struct *napi, int budget) @@ -1824,6 +1861,7 @@ static int vxge_poll_inta(struct napi_struct *napi, int budget) for (i = 0; i < vdev->no_of_vpath; i++) { ring = &vdev->vpaths[i].ring; ring->budget = budget; + ring->pkts_processed = 0; vxge_hw_vpath_poll_rx(ring->handle); pkts_processed += ring->pkts_processed; budget -= ring->pkts_processed; @@ -2054,6 +2092,7 @@ static int vxge_open_vpaths(struct vxgedev *vdev) netdev_get_tx_queue(vdev->ndev, 0); vpath->fifo.indicate_max_pkts = vdev->config.fifo_indicate_max_pkts; + vpath->fifo.tx_vector_no = 0; vpath->ring.rx_vector_no = 0; vpath->ring.rx_csum = vdev->rx_csum; vpath->ring.rx_hwts = vdev->rx_hwts; @@ -2079,6 +2118,61 @@ static int vxge_open_vpaths(struct vxgedev *vdev) return VXGE_HW_OK; } +/** + * adaptive_coalesce_tx_interrupts - Changes the interrupt coalescing + * if the interrupts are not within a range + * @fifo: pointer to transmit fifo structure + * Description: The function changes boundary timer and restriction timer + * value depends on the traffic + * Return Value: None + */ +static void adaptive_coalesce_tx_interrupts(struct vxge_fifo *fifo) +{ + fifo->interrupt_count++; + if (jiffies > fifo->jiffies + HZ / 100) { + struct __vxge_hw_fifo *hw_fifo = fifo->handle; + + fifo->jiffies = jiffies; + if (fifo->interrupt_count > VXGE_T1A_MAX_TX_INTERRUPT_COUNT && + hw_fifo->rtimer != VXGE_TTI_RTIMER_ADAPT_VAL) { + hw_fifo->rtimer = VXGE_TTI_RTIMER_ADAPT_VAL; + vxge_hw_vpath_dynamic_tti_rtimer_set(hw_fifo); + } else if (hw_fifo->rtimer != 0) { + hw_fifo->rtimer = 0; + vxge_hw_vpath_dynamic_tti_rtimer_set(hw_fifo); + } + fifo->interrupt_count = 0; + } +} + +/** + * adaptive_coalesce_rx_interrupts - Changes the interrupt coalescing + * if the interrupts are not within a range + * @ring: pointer to receive ring structure + * Description: The function increases of decreases the packet counts within + * the ranges of traffic utilization, if the interrupts due to this ring are + * not within a fixed range. + * Return Value: Nothing + */ +static void adaptive_coalesce_rx_interrupts(struct vxge_ring *ring) +{ + ring->interrupt_count++; + if (jiffies > ring->jiffies + HZ / 100) { + struct __vxge_hw_ring *hw_ring = ring->handle; + + ring->jiffies = jiffies; + if (ring->interrupt_count > VXGE_T1A_MAX_INTERRUPT_COUNT && + hw_ring->rtimer != VXGE_RTI_RTIMER_ADAPT_VAL) { + hw_ring->rtimer = VXGE_RTI_RTIMER_ADAPT_VAL; + vxge_hw_vpath_dynamic_rti_rtimer_set(hw_ring); + } else if (hw_ring->rtimer != 0) { + hw_ring->rtimer = 0; + vxge_hw_vpath_dynamic_rti_rtimer_set(hw_ring); + } + ring->interrupt_count = 0; + } +} + /* * vxge_isr_napi * @irq: the irq of the device. @@ -2139,24 +2233,39 @@ static irqreturn_t vxge_isr_napi(int irq, void *dev_id) #ifdef CONFIG_PCI_MSI -static irqreturn_t -vxge_tx_msix_handle(int irq, void *dev_id) +static irqreturn_t vxge_tx_msix_handle(int irq, void *dev_id) { struct vxge_fifo *fifo = (struct vxge_fifo *)dev_id; + adaptive_coalesce_tx_interrupts(fifo); + + vxge_hw_channel_msix_mask((struct __vxge_hw_channel *)fifo->handle, + fifo->tx_vector_no); + + vxge_hw_channel_msix_clear((struct __vxge_hw_channel *)fifo->handle, + fifo->tx_vector_no); + VXGE_COMPLETE_VPATH_TX(fifo); + vxge_hw_channel_msix_unmask((struct __vxge_hw_channel *)fifo->handle, + fifo->tx_vector_no); + + mmiowb(); + return IRQ_HANDLED; } -static irqreturn_t -vxge_rx_msix_napi_handle(int irq, void *dev_id) +static irqreturn_t vxge_rx_msix_napi_handle(int irq, void *dev_id) { struct vxge_ring *ring = (struct vxge_ring *)dev_id; - /* MSIX_IDX for Rx is 1 */ + adaptive_coalesce_rx_interrupts(ring); + vxge_hw_channel_msix_mask((struct __vxge_hw_channel *)ring->handle, - ring->rx_vector_no); + ring->rx_vector_no); + + vxge_hw_channel_msix_clear((struct __vxge_hw_channel *)ring->handle, + ring->rx_vector_no); napi_schedule(&ring->napi); return IRQ_HANDLED; @@ -2173,14 +2282,20 @@ vxge_alarm_msix_handle(int irq, void *dev_id) VXGE_HW_VPATH_MSIX_ACTIVE) + VXGE_ALARM_MSIX_ID; for (i = 0; i < vdev->no_of_vpath; i++) { + /* Reduce the chance of loosing alarm interrupts by masking + * the vector. A pending bit will be set if an alarm is + * generated and on unmask the interrupt will be fired. + */ vxge_hw_vpath_msix_mask(vdev->vpaths[i].handle, msix_id); + vxge_hw_vpath_msix_clear(vdev->vpaths[i].handle, msix_id); + mmiowb(); status = vxge_hw_vpath_alarm_process(vdev->vpaths[i].handle, vdev->exec_mode); if (status == VXGE_HW_OK) { - vxge_hw_vpath_msix_unmask(vdev->vpaths[i].handle, - msix_id); + msix_id); + mmiowb(); continue; } vxge_debug_intr(VXGE_ERR, @@ -2299,6 +2414,9 @@ static int vxge_enable_msix(struct vxgedev *vdev) vpath->ring.rx_vector_no = (vpath->device_id * VXGE_HW_VPATH_MSIX_ACTIVE) + 1; + vpath->fifo.tx_vector_no = (vpath->device_id * + VXGE_HW_VPATH_MSIX_ACTIVE); + vxge_hw_vpath_msix_set(vpath->handle, tim_msix_id, VXGE_ALARM_MSIX_ID); } @@ -2474,8 +2592,9 @@ INTA_MODE: "%s:vxge:INTA", vdev->ndev->name); vxge_hw_device_set_intr_type(vdev->devh, VXGE_HW_INTR_MODE_IRQLINE); - vxge_hw_vpath_tti_ci_set(vdev->devh, - vdev->vpaths[0].device_id); + + vxge_hw_vpath_tti_ci_set(vdev->vpaths[0].fifo.handle); + ret = request_irq((int) vdev->pdev->irq, vxge_isr_napi, IRQF_SHARED, vdev->desc[0], vdev); @@ -2745,6 +2864,10 @@ static int vxge_open(struct net_device *dev) } netif_tx_start_all_queues(vdev->ndev); + + /* configure CI */ + vxge_config_ci_for_tti_rti(vdev); + goto out0; out2: @@ -3348,7 +3471,7 @@ static int __devinit vxge_device_register(struct __vxge_hw_device *hldev, vxge_debug_init(VXGE_ERR, "%s: vpath memory allocation failed", vdev->ndev->name); - ret = -ENODEV; + ret = -ENOMEM; goto _out1; } @@ -3369,11 +3492,11 @@ static int __devinit vxge_device_register(struct __vxge_hw_device *hldev, if (vdev->config.gro_enable) ndev->features |= NETIF_F_GRO; - if (register_netdev(ndev)) { + ret = register_netdev(ndev); + if (ret) { vxge_debug_init(vxge_hw_device_trace_level_get(hldev), "%s: %s : device registration failed!", ndev->name, __func__); - ret = -ENODEV; goto _out2; } @@ -3444,6 +3567,11 @@ static void vxge_device_unregister(struct __vxge_hw_device *hldev) /* in 2.6 will call stop() if device is up */ unregister_netdev(dev); + kfree(vdev->vpaths); + + /* we are safe to free it now */ + free_netdev(dev); + vxge_debug_init(vdev->level_trace, "%s: ethernet device unregistered", buf); vxge_debug_entryexit(vdev->level_trace, "%s: %s:%d Exiting...", buf, @@ -3799,7 +3927,7 @@ static void __devinit vxge_device_config_init( break; case MSI_X: - device_config->intr_mode = VXGE_HW_INTR_MODE_MSIX; + device_config->intr_mode = VXGE_HW_INTR_MODE_MSIX_ONE_SHOT; break; } @@ -4335,10 +4463,10 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre) goto _exit1; } - if (pci_request_region(pdev, 0, VXGE_DRIVER_NAME)) { + ret = pci_request_region(pdev, 0, VXGE_DRIVER_NAME); + if (ret) { vxge_debug_init(VXGE_ERR, "%s : request regions failed", __func__); - ret = -ENODEV; goto _exit1; } @@ -4446,7 +4574,7 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre) if (!img[i].is_valid) break; vxge_debug_init(VXGE_TRACE, "%s: EPROM %d, version " - "%d.%d.%d.%d\n", VXGE_DRIVER_NAME, i, + "%d.%d.%d.%d", VXGE_DRIVER_NAME, i, VXGE_EPROM_IMG_MAJOR(img[i].version), VXGE_EPROM_IMG_MINOR(img[i].version), VXGE_EPROM_IMG_FIX(img[i].version), @@ -4643,8 +4771,9 @@ _exit6: _exit5: vxge_device_unregister(hldev); _exit4: - pci_disable_sriov(pdev); + pci_set_drvdata(pdev, NULL); vxge_hw_device_terminate(hldev); + pci_disable_sriov(pdev); _exit3: iounmap(attr.bar0); _exit2: @@ -4655,7 +4784,7 @@ _exit0: kfree(ll_config); kfree(device_config); driver_config->config_dev_cnt--; - pci_set_drvdata(pdev, NULL); + driver_config->total_dev_cnt--; return ret; } @@ -4668,45 +4797,34 @@ _exit0: static void __devexit vxge_remove(struct pci_dev *pdev) { struct __vxge_hw_device *hldev; - struct vxgedev *vdev = NULL; - struct net_device *dev; - int i = 0; + struct vxgedev *vdev; + int i; hldev = pci_get_drvdata(pdev); - if (hldev == NULL) return; - dev = hldev->ndev; - vdev = netdev_priv(dev); + vdev = netdev_priv(hldev->ndev); vxge_debug_entryexit(vdev->level_trace, "%s:%d", __func__, __LINE__); - vxge_debug_init(vdev->level_trace, "%s : removing PCI device...", __func__); - vxge_device_unregister(hldev); - for (i = 0; i < vdev->no_of_vpath; i++) { + for (i = 0; i < vdev->no_of_vpath; i++) vxge_free_mac_add_list(&vdev->vpaths[i]); - vdev->vpaths[i].mcast_addr_cnt = 0; - vdev->vpaths[i].mac_addr_cnt = 0; - } - - kfree(vdev->vpaths); + vxge_device_unregister(hldev); + pci_set_drvdata(pdev, NULL); + /* Do not call pci_disable_sriov here, as it will break child devices */ + vxge_hw_device_terminate(hldev); iounmap(vdev->bar0); - - /* we are safe to free it now */ - free_netdev(dev); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + driver_config->config_dev_cnt--; + driver_config->total_dev_cnt--; vxge_debug_init(vdev->level_trace, "%s:%d Device unregistered", __func__, __LINE__); - - vxge_hw_device_terminate(hldev); - - pci_disable_device(pdev); - pci_release_region(pdev, 0); - pci_set_drvdata(pdev, NULL); vxge_debug_entryexit(vdev->level_trace, "%s:%d Exiting...", __func__, __LINE__); } diff --git a/drivers/net/vxge/vxge-main.h b/drivers/net/vxge/vxge-main.h index 5746fedc356..40474f0da57 100644 --- a/drivers/net/vxge/vxge-main.h +++ b/drivers/net/vxge/vxge-main.h @@ -59,11 +59,13 @@ #define VXGE_TTI_LTIMER_VAL 1000 #define VXGE_T1A_TTI_LTIMER_VAL 80 #define VXGE_TTI_RTIMER_VAL 0 +#define VXGE_TTI_RTIMER_ADAPT_VAL 10 #define VXGE_T1A_TTI_RTIMER_VAL 400 #define VXGE_RTI_BTIMER_VAL 250 #define VXGE_RTI_LTIMER_VAL 100 #define VXGE_RTI_RTIMER_VAL 0 -#define VXGE_FIFO_INDICATE_MAX_PKTS VXGE_DEF_FIFO_LENGTH +#define VXGE_RTI_RTIMER_ADAPT_VAL 15 +#define VXGE_FIFO_INDICATE_MAX_PKTS VXGE_DEF_FIFO_LENGTH #define VXGE_ISR_POLLING_CNT 8 #define VXGE_MAX_CONFIG_DEV 0xFF #define VXGE_EXEC_MODE_DISABLE 0 @@ -107,6 +109,14 @@ #define RTI_T1A_RX_UFC_C 50 #define RTI_T1A_RX_UFC_D 60 +/* + * The interrupt rate is maintained at 3k per second with the moderation + * parameters for most traffic but not all. This is the maximum interrupt + * count allowed per function with INTA or per vector in the case of + * MSI-X in a 10 millisecond time period. Enabled only for Titan 1A. + */ +#define VXGE_T1A_MAX_INTERRUPT_COUNT 100 +#define VXGE_T1A_MAX_TX_INTERRUPT_COUNT 200 /* Milli secs timer period */ #define VXGE_TIMER_DELAY 10000 @@ -247,6 +257,11 @@ struct vxge_fifo { int tx_steering_type; int indicate_max_pkts; + /* Adaptive interrupt moderation parameters used in T1A */ + unsigned long interrupt_count; + unsigned long jiffies; + + u32 tx_vector_no; /* Tx stats */ struct vxge_fifo_stats stats; } ____cacheline_aligned; @@ -271,6 +286,10 @@ struct vxge_ring { */ int driver_id; + /* Adaptive interrupt moderation parameters used in T1A */ + unsigned long interrupt_count; + unsigned long jiffies; + /* copy of the flag indicating whether rx_csum is to be used */ u32 rx_csum:1, rx_hwts:1; @@ -286,7 +305,7 @@ struct vxge_ring { int vlan_tag_strip; struct vlan_group *vlgrp; - int rx_vector_no; + u32 rx_vector_no; enum vxge_hw_status last_status; /* Rx stats */ diff --git a/drivers/net/vxge/vxge-traffic.c b/drivers/net/vxge/vxge-traffic.c index 4c10d6c4075..8674f331311 100644 --- a/drivers/net/vxge/vxge-traffic.c +++ b/drivers/net/vxge/vxge-traffic.c @@ -218,6 +218,68 @@ exit: return status; } +void vxge_hw_vpath_tti_ci_set(struct __vxge_hw_fifo *fifo) +{ + struct vxge_hw_vpath_reg __iomem *vp_reg; + struct vxge_hw_vp_config *config; + u64 val64; + + if (fifo->config->enable != VXGE_HW_FIFO_ENABLE) + return; + + vp_reg = fifo->vp_reg; + config = container_of(fifo->config, struct vxge_hw_vp_config, fifo); + + if (config->tti.timer_ci_en != VXGE_HW_TIM_TIMER_CI_ENABLE) { + config->tti.timer_ci_en = VXGE_HW_TIM_TIMER_CI_ENABLE; + val64 = readq(&vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); + val64 |= VXGE_HW_TIM_CFG1_INT_NUM_TIMER_CI; + fifo->tim_tti_cfg1_saved = val64; + writeq(val64, &vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_TX]); + } +} + +void vxge_hw_vpath_dynamic_rti_ci_set(struct __vxge_hw_ring *ring) +{ + u64 val64 = ring->tim_rti_cfg1_saved; + + val64 |= VXGE_HW_TIM_CFG1_INT_NUM_TIMER_CI; + ring->tim_rti_cfg1_saved = val64; + writeq(val64, &ring->vp_reg->tim_cfg1_int_num[VXGE_HW_VPATH_INTR_RX]); +} + +void vxge_hw_vpath_dynamic_tti_rtimer_set(struct __vxge_hw_fifo *fifo) +{ + u64 val64 = fifo->tim_tti_cfg3_saved; + u64 timer = (fifo->rtimer * 1000) / 272; + + val64 &= ~VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(0x3ffffff); + if (timer) + val64 |= VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(timer) | + VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_EVENT_SF(5); + + writeq(val64, &fifo->vp_reg->tim_cfg3_int_num[VXGE_HW_VPATH_INTR_TX]); + /* tti_cfg3_saved is not updated again because it is + * initialized at one place only - init time. + */ +} + +void vxge_hw_vpath_dynamic_rti_rtimer_set(struct __vxge_hw_ring *ring) +{ + u64 val64 = ring->tim_rti_cfg3_saved; + u64 timer = (ring->rtimer * 1000) / 272; + + val64 &= ~VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(0x3ffffff); + if (timer) + val64 |= VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_VAL(timer) | + VXGE_HW_TIM_CFG3_INT_NUM_RTIMER_EVENT_SF(4); + + writeq(val64, &ring->vp_reg->tim_cfg3_int_num[VXGE_HW_VPATH_INTR_RX]); + /* rti_cfg3_saved is not updated again because it is + * initialized at one place only - init time. + */ +} + /** * vxge_hw_channel_msix_mask - Mask MSIX Vector. * @channeh: Channel for rx or tx handle @@ -254,6 +316,23 @@ vxge_hw_channel_msix_unmask(struct __vxge_hw_channel *channel, int msix_id) } /** + * vxge_hw_channel_msix_clear - Unmask the MSIX Vector. + * @channel: Channel for rx or tx handle + * @msix_id: MSI ID + * + * The function unmasks the msix interrupt for the given msix_id + * if configured in MSIX oneshot mode + * + * Returns: 0 + */ +void vxge_hw_channel_msix_clear(struct __vxge_hw_channel *channel, int msix_id) +{ + __vxge_hw_pio_mem_write32_upper( + (u32) vxge_bVALn(vxge_mBIT(msix_id >> 2), 0, 32), + &channel->common_reg->clr_msix_one_shot_vec[msix_id % 4]); +} + +/** * vxge_hw_device_set_intr_type - Updates the configuration * with new interrupt type. * @hldev: HW device handle. @@ -2191,19 +2270,14 @@ vxge_hw_vpath_msix_set(struct __vxge_hw_vpath_handle *vp, int *tim_msix_id, if (vpath->hldev->config.intr_mode == VXGE_HW_INTR_MODE_MSIX_ONE_SHOT) { __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn( + VXGE_HW_ONE_SHOT_VECT0_EN_ONE_SHOT_VECT0_EN, + 0, 32), &vp_reg->one_shot_vect0_en); + __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn( VXGE_HW_ONE_SHOT_VECT1_EN_ONE_SHOT_VECT1_EN, 0, 32), &vp_reg->one_shot_vect1_en); - } - - if (vpath->hldev->config.intr_mode == - VXGE_HW_INTR_MODE_MSIX_ONE_SHOT) { __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn( VXGE_HW_ONE_SHOT_VECT2_EN_ONE_SHOT_VECT2_EN, 0, 32), &vp_reg->one_shot_vect2_en); - - __vxge_hw_pio_mem_write32_upper((u32)vxge_bVALn( - VXGE_HW_ONE_SHOT_VECT3_EN_ONE_SHOT_VECT3_EN, - 0, 32), &vp_reg->one_shot_vect3_en); } } @@ -2229,6 +2303,32 @@ vxge_hw_vpath_msix_mask(struct __vxge_hw_vpath_handle *vp, int msix_id) } /** + * vxge_hw_vpath_msix_clear - Clear MSIX Vector. + * @vp: Virtual Path handle. + * @msix_id: MSI ID + * + * The function clears the msix interrupt for the given msix_id + * + * Returns: 0, + * Otherwise, VXGE_HW_ERR_WRONG_IRQ if the msix index is out of range + * status. + * See also: + */ +void vxge_hw_vpath_msix_clear(struct __vxge_hw_vpath_handle *vp, int msix_id) +{ + struct __vxge_hw_device *hldev = vp->vpath->hldev; + + if ((hldev->config.intr_mode == VXGE_HW_INTR_MODE_MSIX_ONE_SHOT)) + __vxge_hw_pio_mem_write32_upper( + (u32) vxge_bVALn(vxge_mBIT((msix_id >> 2)), 0, 32), + &hldev->common_reg->clr_msix_one_shot_vec[msix_id % 4]); + else + __vxge_hw_pio_mem_write32_upper( + (u32) vxge_bVALn(vxge_mBIT((msix_id >> 2)), 0, 32), + &hldev->common_reg->clear_msix_mask_vect[msix_id % 4]); +} + +/** * vxge_hw_vpath_msix_unmask - Unmask the MSIX Vector. * @vp: Virtual Path handle. * @msix_id: MSI ID diff --git a/drivers/net/vxge/vxge-traffic.h b/drivers/net/vxge/vxge-traffic.h index d48486d6afa..9d9dfda4c7a 100644 --- a/drivers/net/vxge/vxge-traffic.h +++ b/drivers/net/vxge/vxge-traffic.h @@ -2142,6 +2142,10 @@ void vxge_hw_device_clear_tx_rx( * Virtual Paths */ +void vxge_hw_vpath_dynamic_rti_rtimer_set(struct __vxge_hw_ring *ring); + +void vxge_hw_vpath_dynamic_tti_rtimer_set(struct __vxge_hw_fifo *fifo); + u32 vxge_hw_vpath_id( struct __vxge_hw_vpath_handle *vpath_handle); @@ -2245,6 +2249,8 @@ void vxge_hw_vpath_msix_mask(struct __vxge_hw_vpath_handle *vpath_handle, int msix_id); +void vxge_hw_vpath_msix_clear(struct __vxge_hw_vpath_handle *vp, int msix_id); + void vxge_hw_device_flush_io(struct __vxge_hw_device *devh); void @@ -2270,6 +2276,9 @@ void vxge_hw_channel_msix_unmask(struct __vxge_hw_channel *channelh, int msix_id); void +vxge_hw_channel_msix_clear(struct __vxge_hw_channel *channelh, int msix_id); + +void vxge_hw_channel_dtr_try_complete(struct __vxge_hw_channel *channel, void **dtrh); @@ -2282,7 +2291,8 @@ vxge_hw_channel_dtr_free(struct __vxge_hw_channel *channel, void *dtrh); int vxge_hw_channel_dtr_count(struct __vxge_hw_channel *channel); -void -vxge_hw_vpath_tti_ci_set(struct __vxge_hw_device *hldev, u32 vp_id); +void vxge_hw_vpath_tti_ci_set(struct __vxge_hw_fifo *fifo); + +void vxge_hw_vpath_dynamic_rti_ci_set(struct __vxge_hw_ring *ring); #endif diff --git a/drivers/net/vxge/vxge-version.h b/drivers/net/vxge/vxge-version.h index ad2f99b9bcf..581e21525e8 100644 --- a/drivers/net/vxge/vxge-version.h +++ b/drivers/net/vxge/vxge-version.h @@ -16,8 +16,8 @@ #define VXGE_VERSION_MAJOR "2" #define VXGE_VERSION_MINOR "5" -#define VXGE_VERSION_FIX "1" -#define VXGE_VERSION_BUILD "22082" +#define VXGE_VERSION_FIX "2" +#define VXGE_VERSION_BUILD "22259" #define VXGE_VERSION_FOR "k" #define VXGE_FW_VER(maj, min, bld) (((maj) << 16) + ((min) << 8) + (bld)) |