diff options
author | Michael Chan <mchan@broadcom.com> | 2005-04-21 20:13:59 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-04-21 20:13:59 -0400 |
commit | 7938109fff944e0117976a39946d99d2b0b0250d (patch) | |
tree | 1e1c24ad874db00f827fc6d9087402db6becefc9 /drivers | |
parent | 88b06bc26b87cf0490b0e3faea7fefc7549dd75d (diff) |
[TG3]: Add msi test
Add MSI test for chips that support MSI. If MSI test fails, it will
switch back to INTx mode and will print a message asking the user to
report the failure.
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/tg3.c | 154 |
1 files changed, 151 insertions, 3 deletions
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 2a17af9bd1ce..f65ca3b2da6f 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c | |||
@@ -2996,6 +2996,22 @@ static irqreturn_t tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
2996 | return IRQ_RETVAL(handled); | 2996 | return IRQ_RETVAL(handled); |
2997 | } | 2997 | } |
2998 | 2998 | ||
2999 | /* ISR for interrupt test */ | ||
3000 | static irqreturn_t tg3_test_isr(int irq, void *dev_id, | ||
3001 | struct pt_regs *regs) | ||
3002 | { | ||
3003 | struct net_device *dev = dev_id; | ||
3004 | struct tg3 *tp = netdev_priv(dev); | ||
3005 | struct tg3_hw_status *sblk = tp->hw_status; | ||
3006 | |||
3007 | if (sblk->status & SD_STATUS_UPDATED) { | ||
3008 | tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, | ||
3009 | 0x00000001); | ||
3010 | return IRQ_RETVAL(1); | ||
3011 | } | ||
3012 | return IRQ_RETVAL(0); | ||
3013 | } | ||
3014 | |||
2999 | static int tg3_init_hw(struct tg3 *); | 3015 | static int tg3_init_hw(struct tg3 *); |
3000 | static int tg3_halt(struct tg3 *); | 3016 | static int tg3_halt(struct tg3 *); |
3001 | 3017 | ||
@@ -5796,6 +5812,118 @@ static void tg3_timer(unsigned long __opaque) | |||
5796 | add_timer(&tp->timer); | 5812 | add_timer(&tp->timer); |
5797 | } | 5813 | } |
5798 | 5814 | ||
5815 | static int tg3_test_interrupt(struct tg3 *tp) | ||
5816 | { | ||
5817 | struct net_device *dev = tp->dev; | ||
5818 | int err, i; | ||
5819 | u32 int_mbox = 0; | ||
5820 | |||
5821 | tg3_disable_ints(tp); | ||
5822 | |||
5823 | free_irq(tp->pdev->irq, dev); | ||
5824 | |||
5825 | err = request_irq(tp->pdev->irq, tg3_test_isr, | ||
5826 | SA_SHIRQ, dev->name, dev); | ||
5827 | if (err) | ||
5828 | return err; | ||
5829 | |||
5830 | tg3_enable_ints(tp); | ||
5831 | |||
5832 | tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE | | ||
5833 | HOSTCC_MODE_NOW); | ||
5834 | |||
5835 | for (i = 0; i < 5; i++) { | ||
5836 | int_mbox = tr32(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW); | ||
5837 | if (int_mbox != 0) | ||
5838 | break; | ||
5839 | msleep(10); | ||
5840 | } | ||
5841 | |||
5842 | tg3_disable_ints(tp); | ||
5843 | |||
5844 | free_irq(tp->pdev->irq, dev); | ||
5845 | |||
5846 | if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) | ||
5847 | err = request_irq(tp->pdev->irq, tg3_msi, | ||
5848 | 0, dev->name, dev); | ||
5849 | else | ||
5850 | err = request_irq(tp->pdev->irq, tg3_interrupt, | ||
5851 | SA_SHIRQ, dev->name, dev); | ||
5852 | |||
5853 | if (err) | ||
5854 | return err; | ||
5855 | |||
5856 | if (int_mbox != 0) | ||
5857 | return 0; | ||
5858 | |||
5859 | return -EIO; | ||
5860 | } | ||
5861 | |||
5862 | /* Returns 0 if MSI test succeeds or MSI test fails and INTx mode is | ||
5863 | * successfully restored | ||
5864 | */ | ||
5865 | static int tg3_test_msi(struct tg3 *tp) | ||
5866 | { | ||
5867 | struct net_device *dev = tp->dev; | ||
5868 | int err; | ||
5869 | u16 pci_cmd; | ||
5870 | |||
5871 | if (!(tp->tg3_flags2 & TG3_FLG2_USING_MSI)) | ||
5872 | return 0; | ||
5873 | |||
5874 | /* Turn off SERR reporting in case MSI terminates with Master | ||
5875 | * Abort. | ||
5876 | */ | ||
5877 | pci_read_config_word(tp->pdev, PCI_COMMAND, &pci_cmd); | ||
5878 | pci_write_config_word(tp->pdev, PCI_COMMAND, | ||
5879 | pci_cmd & ~PCI_COMMAND_SERR); | ||
5880 | |||
5881 | err = tg3_test_interrupt(tp); | ||
5882 | |||
5883 | pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); | ||
5884 | |||
5885 | if (!err) | ||
5886 | return 0; | ||
5887 | |||
5888 | /* other failures */ | ||
5889 | if (err != -EIO) | ||
5890 | return err; | ||
5891 | |||
5892 | /* MSI test failed, go back to INTx mode */ | ||
5893 | printk(KERN_WARNING PFX "%s: No interrupt was generated using MSI, " | ||
5894 | "switching to INTx mode. Please report this failure to " | ||
5895 | "the PCI maintainer and include system chipset information.\n", | ||
5896 | tp->dev->name); | ||
5897 | |||
5898 | free_irq(tp->pdev->irq, dev); | ||
5899 | pci_disable_msi(tp->pdev); | ||
5900 | |||
5901 | tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI; | ||
5902 | |||
5903 | err = request_irq(tp->pdev->irq, tg3_interrupt, | ||
5904 | SA_SHIRQ, dev->name, dev); | ||
5905 | |||
5906 | if (err) | ||
5907 | return err; | ||
5908 | |||
5909 | /* Need to reset the chip because the MSI cycle may have terminated | ||
5910 | * with Master Abort. | ||
5911 | */ | ||
5912 | spin_lock_irq(&tp->lock); | ||
5913 | spin_lock(&tp->tx_lock); | ||
5914 | |||
5915 | tg3_halt(tp); | ||
5916 | err = tg3_init_hw(tp); | ||
5917 | |||
5918 | spin_unlock(&tp->tx_lock); | ||
5919 | spin_unlock_irq(&tp->lock); | ||
5920 | |||
5921 | if (err) | ||
5922 | free_irq(tp->pdev->irq, dev); | ||
5923 | |||
5924 | return err; | ||
5925 | } | ||
5926 | |||
5799 | static int tg3_open(struct net_device *dev) | 5927 | static int tg3_open(struct net_device *dev) |
5800 | { | 5928 | { |
5801 | struct tg3 *tp = netdev_priv(dev); | 5929 | struct tg3 *tp = netdev_priv(dev); |
@@ -5860,9 +5988,6 @@ static int tg3_open(struct net_device *dev) | |||
5860 | tp->timer.expires = jiffies + tp->timer_offset; | 5988 | tp->timer.expires = jiffies + tp->timer_offset; |
5861 | tp->timer.data = (unsigned long) tp; | 5989 | tp->timer.data = (unsigned long) tp; |
5862 | tp->timer.function = tg3_timer; | 5990 | tp->timer.function = tg3_timer; |
5863 | add_timer(&tp->timer); | ||
5864 | |||
5865 | tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; | ||
5866 | } | 5991 | } |
5867 | 5992 | ||
5868 | spin_unlock(&tp->tx_lock); | 5993 | spin_unlock(&tp->tx_lock); |
@@ -5878,9 +6003,32 @@ static int tg3_open(struct net_device *dev) | |||
5878 | return err; | 6003 | return err; |
5879 | } | 6004 | } |
5880 | 6005 | ||
6006 | if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { | ||
6007 | err = tg3_test_msi(tp); | ||
6008 | if (err) { | ||
6009 | spin_lock_irq(&tp->lock); | ||
6010 | spin_lock(&tp->tx_lock); | ||
6011 | |||
6012 | if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) { | ||
6013 | pci_disable_msi(tp->pdev); | ||
6014 | tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI; | ||
6015 | } | ||
6016 | tg3_halt(tp); | ||
6017 | tg3_free_rings(tp); | ||
6018 | tg3_free_consistent(tp); | ||
6019 | |||
6020 | spin_unlock(&tp->tx_lock); | ||
6021 | spin_unlock_irq(&tp->lock); | ||
6022 | |||
6023 | return err; | ||
6024 | } | ||
6025 | } | ||
6026 | |||
5881 | spin_lock_irq(&tp->lock); | 6027 | spin_lock_irq(&tp->lock); |
5882 | spin_lock(&tp->tx_lock); | 6028 | spin_lock(&tp->tx_lock); |
5883 | 6029 | ||
6030 | add_timer(&tp->timer); | ||
6031 | tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; | ||
5884 | tg3_enable_ints(tp); | 6032 | tg3_enable_ints(tp); |
5885 | 6033 | ||
5886 | spin_unlock(&tp->tx_lock); | 6034 | spin_unlock(&tp->tx_lock); |