diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-03-20 18:48:19 -0500 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-03-21 16:00:52 -0500 |
commit | fb2690a9bfa330aff3de29cbdde526591ac90dce (patch) | |
tree | e9a070be2062a0221828a29d7499d8e65ce57bba | |
parent | 77b3d6a2d56be5af87ffae5bb78a39c847d49f99 (diff) |
[PATCH] sky2: add MSI support
Add MSI support to sky2 driver.
Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/net/sky2.c | 81 | ||||
-rw-r--r-- | drivers/net/sky2.h | 2 |
2 files changed, 79 insertions, 4 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 7e3353f55c38..2e29972b8d0b 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -92,6 +92,10 @@ static int copybreak __read_mostly = 256; | |||
92 | module_param(copybreak, int, 0); | 92 | module_param(copybreak, int, 0); |
93 | MODULE_PARM_DESC(copybreak, "Receive copy threshold"); | 93 | MODULE_PARM_DESC(copybreak, "Receive copy threshold"); |
94 | 94 | ||
95 | static int disable_msi = 0; | ||
96 | module_param(disable_msi, int, 0); | ||
97 | MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); | ||
98 | |||
95 | static const struct pci_device_id sky2_id_table[] = { | 99 | static const struct pci_device_id sky2_id_table[] = { |
96 | { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, | 100 | { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, |
97 | { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, | 101 | { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, |
@@ -2045,7 +2049,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) | |||
2045 | struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw; | 2049 | struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw; |
2046 | int work_limit = min(dev0->quota, *budget); | 2050 | int work_limit = min(dev0->quota, *budget); |
2047 | int work_done = 0; | 2051 | int work_done = 0; |
2048 | u32 status = sky2_read32(hw, B0_ISRC); | 2052 | u32 status = sky2_read32(hw, B0_Y2_SP_EISR); |
2049 | 2053 | ||
2050 | if (status & Y2_IS_HW_ERR) | 2054 | if (status & Y2_IS_HW_ERR) |
2051 | sky2_hw_intr(hw); | 2055 | sky2_hw_intr(hw); |
@@ -2075,8 +2079,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) | |||
2075 | 2079 | ||
2076 | netif_rx_complete(dev0); | 2080 | netif_rx_complete(dev0); |
2077 | 2081 | ||
2078 | /* Ack interrupt and re-enable */ | 2082 | status = sky2_read32(hw, B0_Y2_SP_LISR); |
2079 | sky2_write32(hw, B0_Y2_SP_ICR, 2); | ||
2080 | return 0; | 2083 | return 0; |
2081 | } | 2084 | } |
2082 | 2085 | ||
@@ -3001,6 +3004,66 @@ static void __devinit sky2_show_addr(struct net_device *dev) | |||
3001 | dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); | 3004 | dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); |
3002 | } | 3005 | } |
3003 | 3006 | ||
3007 | /* Handle software interrupt used during MSI test */ | ||
3008 | static irqreturn_t __devinit sky2_test_intr(int irq, void *dev_id, | ||
3009 | struct pt_regs *regs) | ||
3010 | { | ||
3011 | struct sky2_hw *hw = dev_id; | ||
3012 | u32 status = sky2_read32(hw, B0_Y2_SP_ISRC2); | ||
3013 | |||
3014 | if (status == 0) | ||
3015 | return IRQ_NONE; | ||
3016 | |||
3017 | if (status & Y2_IS_IRQ_SW) { | ||
3018 | hw->msi_detected = 1; | ||
3019 | wake_up(&hw->msi_wait); | ||
3020 | sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ); | ||
3021 | } | ||
3022 | sky2_write32(hw, B0_Y2_SP_ICR, 2); | ||
3023 | |||
3024 | return IRQ_HANDLED; | ||
3025 | } | ||
3026 | |||
3027 | /* Test interrupt path by forcing a a software IRQ */ | ||
3028 | static int __devinit sky2_test_msi(struct sky2_hw *hw) | ||
3029 | { | ||
3030 | struct pci_dev *pdev = hw->pdev; | ||
3031 | int err; | ||
3032 | |||
3033 | sky2_write32(hw, B0_IMSK, Y2_IS_IRQ_SW); | ||
3034 | |||
3035 | err = request_irq(pdev->irq, sky2_test_intr, SA_SHIRQ, DRV_NAME, hw); | ||
3036 | if (err) { | ||
3037 | printk(KERN_ERR PFX "%s: cannot assign irq %d\n", | ||
3038 | pci_name(pdev), pdev->irq); | ||
3039 | return err; | ||
3040 | } | ||
3041 | |||
3042 | init_waitqueue_head (&hw->msi_wait); | ||
3043 | |||
3044 | sky2_write8(hw, B0_CTST, CS_ST_SW_IRQ); | ||
3045 | wmb(); | ||
3046 | |||
3047 | wait_event_timeout(hw->msi_wait, hw->msi_detected, HZ/10); | ||
3048 | |||
3049 | if (!hw->msi_detected) { | ||
3050 | /* MSI test failed, go back to INTx mode */ | ||
3051 | printk(KERN_WARNING PFX "%s: No interrupt was generated using MSI, " | ||
3052 | "switching to INTx mode. Please report this failure to " | ||
3053 | "the PCI maintainer and include system chipset information.\n", | ||
3054 | pci_name(pdev)); | ||
3055 | |||
3056 | err = -EOPNOTSUPP; | ||
3057 | sky2_write8(hw, B0_CTST, CS_CL_SW_IRQ); | ||
3058 | } | ||
3059 | |||
3060 | sky2_write32(hw, B0_IMSK, 0); | ||
3061 | |||
3062 | free_irq(pdev->irq, hw); | ||
3063 | |||
3064 | return err; | ||
3065 | } | ||
3066 | |||
3004 | static int __devinit sky2_probe(struct pci_dev *pdev, | 3067 | static int __devinit sky2_probe(struct pci_dev *pdev, |
3005 | const struct pci_device_id *ent) | 3068 | const struct pci_device_id *ent) |
3006 | { | 3069 | { |
@@ -3121,7 +3184,15 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
3121 | } | 3184 | } |
3122 | } | 3185 | } |
3123 | 3186 | ||
3124 | err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ, DRV_NAME, hw); | 3187 | if (!disable_msi && pci_enable_msi(pdev) == 0) { |
3188 | err = sky2_test_msi(hw); | ||
3189 | if (err == -EOPNOTSUPP) | ||
3190 | pci_disable_msi(pdev); | ||
3191 | else if (err) | ||
3192 | goto err_out_unregister; | ||
3193 | } | ||
3194 | |||
3195 | err = request_irq(pdev->irq, sky2_intr, SA_SHIRQ, DRV_NAME, hw); | ||
3125 | if (err) { | 3196 | if (err) { |
3126 | printk(KERN_ERR PFX "%s: cannot assign irq %d\n", | 3197 | printk(KERN_ERR PFX "%s: cannot assign irq %d\n", |
3127 | pci_name(pdev), pdev->irq); | 3198 | pci_name(pdev), pdev->irq); |
@@ -3135,6 +3206,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, | |||
3135 | return 0; | 3206 | return 0; |
3136 | 3207 | ||
3137 | err_out_unregister: | 3208 | err_out_unregister: |
3209 | pci_disable_msi(pdev); | ||
3138 | if (dev1) { | 3210 | if (dev1) { |
3139 | unregister_netdev(dev1); | 3211 | unregister_netdev(dev1); |
3140 | free_netdev(dev1); | 3212 | free_netdev(dev1); |
@@ -3177,6 +3249,7 @@ static void __devexit sky2_remove(struct pci_dev *pdev) | |||
3177 | sky2_read8(hw, B0_CTST); | 3249 | sky2_read8(hw, B0_CTST); |
3178 | 3250 | ||
3179 | free_irq(pdev->irq, hw); | 3251 | free_irq(pdev->irq, hw); |
3252 | pci_disable_msi(pdev); | ||
3180 | pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); | 3253 | pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); |
3181 | pci_release_regions(pdev); | 3254 | pci_release_regions(pdev); |
3182 | pci_disable_device(pdev); | 3255 | pci_disable_device(pdev); |
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index db362b69b0bb..50e9f7d38bf3 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h | |||
@@ -1879,6 +1879,8 @@ struct sky2_hw { | |||
1879 | struct sky2_status_le *st_le; | 1879 | struct sky2_status_le *st_le; |
1880 | u32 st_idx; | 1880 | u32 st_idx; |
1881 | dma_addr_t st_dma; | 1881 | dma_addr_t st_dma; |
1882 | int msi_detected; | ||
1883 | wait_queue_head_t msi_wait; | ||
1882 | }; | 1884 | }; |
1883 | 1885 | ||
1884 | /* Register accessor for memory mapped device */ | 1886 | /* Register accessor for memory mapped device */ |