diff options
author | Stephane Grosjean <s.grosjean@peak-system.com> | 2012-02-01 05:05:48 -0500 |
---|---|---|
committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2012-02-02 18:26:27 -0500 |
commit | 29830406415c227a54af429d7b300aabd4754237 (patch) | |
tree | 9cc1aa1db2647819273ff7b8b74840327da534c0 /drivers/net/can | |
parent | d0a71a7e6de0e0ce9f86c8ba6e13414a9df63e0b (diff) |
can: peak_pci: Fix the way channels are linked together
Change the way channels objects are linked together by peak_pci_probe()
avoiding any kernel oops when driver is removed. Side effect is that
the list is now browsed from last to first channel.
Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
Diffstat (limited to 'drivers/net/can')
-rw-r--r-- | drivers/net/can/sja1000/peak_pci.c | 23 |
1 files changed, 9 insertions, 14 deletions
diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index 2c7f5036f570..214795945bc4 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c | |||
@@ -39,9 +39,9 @@ MODULE_LICENSE("GPL v2"); | |||
39 | #define DRV_NAME "peak_pci" | 39 | #define DRV_NAME "peak_pci" |
40 | 40 | ||
41 | struct peak_pci_chan { | 41 | struct peak_pci_chan { |
42 | void __iomem *cfg_base; /* Common for all channels */ | 42 | void __iomem *cfg_base; /* Common for all channels */ |
43 | struct net_device *next_dev; /* Chain of network devices */ | 43 | struct net_device *prev_dev; /* Chain of network devices */ |
44 | u16 icr_mask; /* Interrupt mask for fast ack */ | 44 | u16 icr_mask; /* Interrupt mask for fast ack */ |
45 | }; | 45 | }; |
46 | 46 | ||
47 | #define PEAK_PCI_CAN_CLOCK (16000000 / 2) | 47 | #define PEAK_PCI_CAN_CLOCK (16000000 / 2) |
@@ -98,7 +98,7 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, | |||
98 | { | 98 | { |
99 | struct sja1000_priv *priv; | 99 | struct sja1000_priv *priv; |
100 | struct peak_pci_chan *chan; | 100 | struct peak_pci_chan *chan; |
101 | struct net_device *dev, *dev0 = NULL; | 101 | struct net_device *dev; |
102 | void __iomem *cfg_base, *reg_base; | 102 | void __iomem *cfg_base, *reg_base; |
103 | u16 sub_sys_id, icr; | 103 | u16 sub_sys_id, icr; |
104 | int i, err, channels; | 104 | int i, err, channels; |
@@ -196,18 +196,14 @@ static int __devinit peak_pci_probe(struct pci_dev *pdev, | |||
196 | } | 196 | } |
197 | 197 | ||
198 | /* Create chain of SJA1000 devices */ | 198 | /* Create chain of SJA1000 devices */ |
199 | if (i == 0) | 199 | chan->prev_dev = pci_get_drvdata(pdev); |
200 | dev0 = dev; | 200 | pci_set_drvdata(pdev, dev); |
201 | else | ||
202 | chan->next_dev = dev; | ||
203 | 201 | ||
204 | dev_info(&pdev->dev, | 202 | dev_info(&pdev->dev, |
205 | "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", | 203 | "%s at reg_base=0x%p cfg_base=0x%p irq=%d\n", |
206 | dev->name, priv->reg_base, chan->cfg_base, dev->irq); | 204 | dev->name, priv->reg_base, chan->cfg_base, dev->irq); |
207 | } | 205 | } |
208 | 206 | ||
209 | pci_set_drvdata(pdev, dev0); | ||
210 | |||
211 | /* Enable interrupts */ | 207 | /* Enable interrupts */ |
212 | writew(icr, cfg_base + PITA_ICR + 2); | 208 | writew(icr, cfg_base + PITA_ICR + 2); |
213 | 209 | ||
@@ -217,12 +213,11 @@ failure_remove_channels: | |||
217 | /* Disable interrupts */ | 213 | /* Disable interrupts */ |
218 | writew(0x0, cfg_base + PITA_ICR + 2); | 214 | writew(0x0, cfg_base + PITA_ICR + 2); |
219 | 215 | ||
220 | for (dev = dev0; dev; dev = chan->next_dev) { | 216 | for (dev = pci_get_drvdata(pdev); dev; dev = chan->prev_dev) { |
221 | unregister_sja1000dev(dev); | 217 | unregister_sja1000dev(dev); |
222 | free_sja1000dev(dev); | 218 | free_sja1000dev(dev); |
223 | priv = netdev_priv(dev); | 219 | priv = netdev_priv(dev); |
224 | chan = priv->priv; | 220 | chan = priv->priv; |
225 | dev = chan->next_dev; | ||
226 | } | 221 | } |
227 | 222 | ||
228 | pci_iounmap(pdev, reg_base); | 223 | pci_iounmap(pdev, reg_base); |
@@ -241,7 +236,7 @@ failure_disable_pci: | |||
241 | 236 | ||
242 | static void __devexit peak_pci_remove(struct pci_dev *pdev) | 237 | static void __devexit peak_pci_remove(struct pci_dev *pdev) |
243 | { | 238 | { |
244 | struct net_device *dev = pci_get_drvdata(pdev); /* First device */ | 239 | struct net_device *dev = pci_get_drvdata(pdev); /* Last device */ |
245 | struct sja1000_priv *priv = netdev_priv(dev); | 240 | struct sja1000_priv *priv = netdev_priv(dev); |
246 | struct peak_pci_chan *chan = priv->priv; | 241 | struct peak_pci_chan *chan = priv->priv; |
247 | void __iomem *cfg_base = chan->cfg_base; | 242 | void __iomem *cfg_base = chan->cfg_base; |
@@ -255,7 +250,7 @@ static void __devexit peak_pci_remove(struct pci_dev *pdev) | |||
255 | dev_info(&pdev->dev, "removing device %s\n", dev->name); | 250 | dev_info(&pdev->dev, "removing device %s\n", dev->name); |
256 | unregister_sja1000dev(dev); | 251 | unregister_sja1000dev(dev); |
257 | free_sja1000dev(dev); | 252 | free_sja1000dev(dev); |
258 | dev = chan->next_dev; | 253 | dev = chan->prev_dev; |
259 | if (!dev) | 254 | if (!dev) |
260 | break; | 255 | break; |
261 | priv = netdev_priv(dev); | 256 | priv = netdev_priv(dev); |