diff mbox

bc47xx : fix ssb irq setup

Message ID 4A11DBD4.7070706@free.fr
State Not Applicable, archived
Headers show

Commit Message

Matthieu CASTET May 18, 2009, 10:06 p.m. UTC
Hi,

the current ssb irq setup (in ssb_mipscore_init) have some problem :
it configure some device on some irq without checking that the irq is 
not taken by an other device.

For example in my case PCI host is on irq 0 and IPSEC on irq 3.
The current code :
- store in dev->irq that IPSEC irq is 3+2
- do a set_irq 0->3 on PCI host

But now IPSEC irq is not routed anymore to the mips code and dev->irq is 
wrong. This cause problem described in [1].

This patch try to solve the problem by making set_irq configure the 
device we want to take the irq on the shared irq0.
The previous example become :
- store in dev->irq that IPSEC irq is 3+2
- do a set_irq 0->3 on PCI host :
   - irq 3 is already taken by IPSEC. do a set_irq 3->0 on IPSEC


I also added some code to print the irq configuration before and after 
irq setup to allow easier debugging. And I add extra checking in 
ssb_mips_irq to report device without irq or device with not routed irq.


[1] http://www.danm.de/files/src/bcm5365p/REPORTED_DEVICES

Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>

Comments

Michael Buesch May 19, 2009, 1:19 p.m. UTC | #1
On Tuesday 19 May 2009 00:06:12 matthieu castet wrote:
> Hi,
> 
> the current ssb irq setup (in ssb_mipscore_init) have some problem :
> it configure some device on some irq without checking that the irq is 
> not taken by an other device.
> 
> For example in my case PCI host is on irq 0 and IPSEC on irq 3.
> The current code :
> - store in dev->irq that IPSEC irq is 3+2
> - do a set_irq 0->3 on PCI host
> 
> But now IPSEC irq is not routed anymore to the mips code and dev->irq is 
> wrong. This cause problem described in [1].
> 
> This patch try to solve the problem by making set_irq configure the 
> device we want to take the irq on the shared irq0.
> The previous example become :
> - store in dev->irq that IPSEC irq is 3+2
> - do a set_irq 0->3 on PCI host :
>    - irq 3 is already taken by IPSEC. do a set_irq 3->0 on IPSEC
> 
> 
> I also added some code to print the irq configuration before and after 
> irq setup to allow easier debugging. And I add extra checking in 
> ssb_mips_irq to report device without irq or device with not routed irq.
> 
> 
> [1] http://www.danm.de/files/src/bcm5365p/REPORTED_DEVICES
> 
> Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>
> 

If this works on all devices, I'm OK with this. Please submit to linville@tuxdriver.com
You can add my ack.
Matthieu CASTET May 25, 2009, 5:55 p.m. UTC | #2
Michael Buesch wrote:
> On Tuesday 19 May 2009 00:06:12 matthieu castet wrote:
>> Hi,
>>
>>
>> [1] http://www.danm.de/files/src/bcm5365p/REPORTED_DEVICES
>>
>> Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>
>>
> 
> If this works on all devices, I'm OK with this. Please submit to linville@tuxdriver.com
> You can add my ack.
> 
Well I have only a wl500gd.
I have submit it on openwrt project in order to test in more devices.


Matthieu
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Florian Fainelli June 11, 2009, 7:59 a.m. UTC | #3
Hi Matthieu, Michael,

Le Monday 25 May 2009 19:55:58 matthieu castet, vous avez écrit :
> Michael Buesch wrote:
> > On Tuesday 19 May 2009 00:06:12 matthieu castet wrote:
> >> Hi,
> >>
> >>
> >> [1] http://www.danm.de/files/src/bcm5365p/REPORTED_DEVICES
> >>
> >> Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>
> >
> > If this works on all devices, I'm OK with this. Please submit to
> > linville@tuxdriver.com You can add my ack.
>
> Well I have only a wl500gd.
> I have submit it on openwrt project in order to test in more devices.

It makes the IPsec core work on my Netgear WGT634U and I did not see any 
regression on a Linksys WRT54GS.

Tested-by: Florian Fainelli <florian@openwrt.org>
Florian Fainelli June 11, 2009, 8:09 a.m. UTC | #4
Le Thursday 11 June 2009 09:59:36 Florian Fainelli, vous avez écrit :
> Hi Matthieu, Michael,
>
> Le Monday 25 May 2009 19:55:58 matthieu castet, vous avez écrit :
> > Michael Buesch wrote:
> > > On Tuesday 19 May 2009 00:06:12 matthieu castet wrote:
> > >> Hi,
> > >>
> > >>
> > >> [1] http://www.danm.de/files/src/bcm5365p/REPORTED_DEVICES
> > >>
> > >> Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>
> > >
> > > If this works on all devices, I'm OK with this. Please submit to
> > > linville@tuxdriver.com You can add my ack.
> >
> > Well I have only a wl500gd.
> > I have submit it on openwrt project in order to test in more devices.
>
> It makes the IPsec core work on my Netgear WGT634U and I did not see any
> regression on a Linksys WRT54GS.
>
> Tested-by: Florian Fainelli <florian@openwrt.org>

One minor thing, please remove the dump_irqs call, it is convenient for 
debugging to print the IRQ routing, but I find it a little too verbose for 
production. This can be a follow-up patch if you prefer not to respin it.
Matthieu CASTET June 11, 2009, 9:23 a.m. UTC | #5
Quoting Florian Fainelli <florian@openwrt.org>:
> Le Thursday 11 June 2009 09:59:36 Florian Fainelli, vous avez écrit :
> > Hi Matthieu, Michael,
> >
> > Le Monday 25 May 2009 19:55:58 matthieu castet, vous avez écrit :
> > > Michael Buesch wrote:
> > > > On Tuesday 19 May 2009 00:06:12 matthieu castet wrote:
> > > >> Hi,
> > > >>
> > > >>
> > > >> [1] http://www.danm.de/files/src/bcm5365p/REPORTED_DEVICES
> > > >>
> > > >> Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr>
> > > >
> > > > If this works on all devices, I'm OK with this. Please submit to
> > > > linville@tuxdriver.com You can add my ack.
> > >
> > > Well I have only a wl500gd.
> > > I have submit it on openwrt project in order to test in more devices.
> >
> > It makes the IPsec core work on my Netgear WGT634U and I did not see any
> > regression on a Linksys WRT54GS.
> >
> > Tested-by: Florian Fainelli <florian@openwrt.org>
>
> One minor thing, please remove the dump_irqs call, it is convenient for
> debugging to print the IRQ routing, but I find it a little too verbose for
> production. This can be a follow-up patch if you prefer not to respin it.

Well all x86 does something similar, with acpi dumping the pci interrupt Routing
Table even in production kernel.
But it can removed if people don't want it.


Matthieu
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
index 3fd3e3b..6acf43f 100644
--- a/drivers/ssb/driver_mipscore.c
+++ b/drivers/ssb/driver_mipscore.c
@@ -49,29 +49,54 @@  static const u32 ipsflag_irq_shift[] = {
 
 static inline u32 ssb_irqflag(struct ssb_device *dev)
 {
-	return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
+	u32 tpsflag = ssb_read32(dev, SSB_TPSFLAG);
+	if (tpsflag)
+		return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
+	else
+		/* not irq supported */
+		return 0x3f;
+}
+
+static struct ssb_device *find_device(struct ssb_device *rdev, int irqflag)
+{
+	struct ssb_bus *bus = rdev->bus;
+	int i;
+	for (i = 0; i < bus->nr_devices; i++) {
+		struct ssb_device *dev;
+		dev = &(bus->devices[i]);
+		if (ssb_irqflag(dev) == irqflag)
+			return dev;
+	}
+	return NULL;
 }
 
 /* Get the MIPS IRQ assignment for a specified device.
  * If unassigned, 0 is returned.
+ * If disabled, 5 is returned.
+ * If not supported, 6 is returned.
  */
 unsigned int ssb_mips_irq(struct ssb_device *dev)
 {
 	struct ssb_bus *bus = dev->bus;
+	struct ssb_device *mdev = bus->mipscore.dev;
 	u32 irqflag;
 	u32 ipsflag;
 	u32 tmp;
 	unsigned int irq;
 
 	irqflag = ssb_irqflag(dev);
+	if (irqflag == 0x3f)
+		return 6;
 	ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG);
 	for (irq = 1; irq <= 4; irq++) {
 		tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]);
 		if (tmp == irqflag)
 			break;
 	}
-	if (irq	== 5)
-		irq = 0;
+	if (irq	== 5) {
+		if ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))
+			irq = 0;
+	}
 
 	return irq;
 }
@@ -97,25 +122,56 @@  static void set_irq(struct ssb_device *dev, unsigned int irq)
 	struct ssb_device *mdev = bus->mipscore.dev;
 	u32 irqflag = ssb_irqflag(dev);
 
+	BUG_ON(oldirq == 6);
+
 	dev->irq = irq + 2;
 
-	ssb_dprintk(KERN_INFO PFX
-		    "set_irq: core 0x%04x, irq %d => %d\n",
-		    dev->id.coreid, oldirq, irq);
 	/* clear the old irq */
 	if (oldirq == 0)
 		ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
-	else
+	else if (oldirq != 5)
 		clear_irq(bus, oldirq);
 
 	/* assign the new one */
 	if (irq == 0) {
 		ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) | ssb_read32(mdev, SSB_INTVEC)));
 	} else {
+		u32 ipsflag = ssb_read32(mdev, SSB_IPSFLAG);
+		if ((ipsflag & ipsflag_irq_mask[irq]) != ipsflag_irq_mask[irq]) {
+			u32 oldipsflag = (ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq];
+			struct ssb_device *olddev = find_device(dev, oldipsflag);
+			if (olddev)
+				set_irq(olddev, 0);
+		}
 		irqflag <<= ipsflag_irq_shift[irq];
-		irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]);
+		irqflag |= (ipsflag & ~ipsflag_irq_mask[irq]);
 		ssb_write32(mdev, SSB_IPSFLAG, irqflag);
 	}
+	ssb_dprintk(KERN_INFO PFX
+		    "set_irq: core 0x%04x, irq %d => %d\n",
+		    dev->id.coreid, oldirq+2, irq+2);
+}
+
+static void print_irq(struct ssb_device *dev, unsigned int irq)
+{
+	int i;
+	static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
+	ssb_dprintk(KERN_INFO PFX
+		"core 0x%04x, irq :", dev->id.coreid);
+	for (i = 0; i <= 6; i++) {
+		ssb_dprintk(" %s%s", irq_name[i], i==irq?"*":" ");
+	}
+	ssb_dprintk("\n");
+}
+
+static void dump_irq(struct ssb_bus *bus)
+{
+	int i;
+	for (i = 0; i < bus->nr_devices; i++) {
+		struct ssb_device *dev;
+		dev = &(bus->devices[i]);
+		print_irq(dev, ssb_mips_irq(dev));
+	}
 }
 
 static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
@@ -195,18 +251,26 @@  void ssb_mipscore_init(struct ssb_mipscore *mcore)
 	else if (bus->chipco.dev)
 		ssb_chipco_timing_init(&bus->chipco, ns);
 
+	dump_irq(bus);
 	/* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */
 	for (irq = 2, i = 0; i < bus->nr_devices; i++) {
+		int mips_irq;
 		dev = &(bus->devices[i]);
-		dev->irq = ssb_mips_irq(dev) + 2;
+		mips_irq = ssb_mips_irq(dev);
+		if (mips_irq > 4)
+			dev->irq = 0;
+		else
+			dev->irq = mips_irq + 2;
+		if (dev->irq > 5)
+			continue;
 		switch (dev->id.coreid) {
 		case SSB_DEV_USB11_HOST:
 			/* shouldn't need a separate irq line for non-4710, most of them have a proper
 			 * external usb controller on the pci */
 			if ((bus->chip_id == 0x4710) && (irq <= 4)) {
 				set_irq(dev, irq++);
-				break;
 			}
+			break;
 			/* fallthrough */
 		case SSB_DEV_PCI:
 		case SSB_DEV_ETHERNET:
@@ -220,6 +284,8 @@  void ssb_mipscore_init(struct ssb_mipscore *mcore)
 			}
 		}
 	}
+	ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n");
+	dump_irq(bus);
 
 	ssb_mips_serial_init(mcore);
 	ssb_mips_flash_detect(mcore);