[brcm47xx] Port to kernel 2.6.30.
[openwrt.git] / target / linux / brcm47xx / patches-2.6.30 / 816-ssb_fix_irq_setup.patch
1 the current ssb irq setup (in ssb_mipscore_init) have some problem :
2 it configure some device on some irq without checking that the irq is not taken by an other device.
3
4 For example in my case PCI host is on irq 0 and IPSEC on irq 3.
5 The current code :
6 - store in dev->irq that IPSEC irq is 3+2
7 - do a set_irq 0->3 on PCI host
8
9 But now IPSEC irq is not routed anymore to the mips code and dev->irq is wrong. This cause problem described in [1].
10
11 This patch try to solve the problem by making set_irq configure the device we want to take the irq on the shared irq0.
12 The previous example become :
13 - store in dev->irq that IPSEC irq is 3+2
14 - do a set_irq 0->3 on PCI host :
15   - irq 3 is already taken by IPSEC. do a set_irq 3->0 on IPSEC
16
17
18 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.
19
20
21 [1] http://www.danm.de/files/src/bcm5365p/REPORTED_DEVICES
22
23 Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr> 
24 --- a/drivers/ssb/driver_mipscore.c
25 +++ b/drivers/ssb/driver_mipscore.c
26 @@ -49,29 +49,54 @@ static const u32 ipsflag_irq_shift[] = {
27  
28  static inline u32 ssb_irqflag(struct ssb_device *dev)
29  {
30 -       return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
31 +       u32 tpsflag = ssb_read32(dev, SSB_TPSFLAG);
32 +       if (tpsflag)
33 +               return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
34 +       else
35 +               /* not irq supported */
36 +               return 0x3f;
37 +}
38 +
39 +static struct ssb_device *find_device(struct ssb_device *rdev, int irqflag)
40 +{
41 +       struct ssb_bus *bus = rdev->bus;
42 +       int i;
43 +       for (i = 0; i < bus->nr_devices; i++) {
44 +               struct ssb_device *dev;
45 +               dev = &(bus->devices[i]);
46 +               if (ssb_irqflag(dev) == irqflag)
47 +                       return dev;
48 +       }
49 +       return NULL;
50  }
51  
52  /* Get the MIPS IRQ assignment for a specified device.
53   * If unassigned, 0 is returned.
54 + * If disabled, 5 is returned.
55 + * If not supported, 6 is returned.
56   */
57  unsigned int ssb_mips_irq(struct ssb_device *dev)
58  {
59         struct ssb_bus *bus = dev->bus;
60 +       struct ssb_device *mdev = bus->mipscore.dev;
61         u32 irqflag;
62         u32 ipsflag;
63         u32 tmp;
64         unsigned int irq;
65  
66         irqflag = ssb_irqflag(dev);
67 +       if (irqflag == 0x3f)
68 +               return 6;
69         ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG);
70         for (irq = 1; irq <= 4; irq++) {
71                 tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]);
72                 if (tmp == irqflag)
73                         break;
74         }
75 -       if (irq == 5)
76 -               irq = 0;
77 +       if (irq == 5) {
78 +               if ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))
79 +                       irq = 0;
80 +       }
81  
82         return irq;
83  }
84 @@ -97,25 +122,56 @@ static void set_irq(struct ssb_device *d
85         struct ssb_device *mdev = bus->mipscore.dev;
86         u32 irqflag = ssb_irqflag(dev);
87  
88 +       BUG_ON(oldirq == 6);
89 +
90         dev->irq = irq + 2;
91  
92 -       ssb_dprintk(KERN_INFO PFX
93 -                   "set_irq: core 0x%04x, irq %d => %d\n",
94 -                   dev->id.coreid, oldirq, irq);
95         /* clear the old irq */
96         if (oldirq == 0)
97                 ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
98 -       else
99 +       else if (oldirq != 5)
100                 clear_irq(bus, oldirq);
101  
102         /* assign the new one */
103         if (irq == 0) {
104                 ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) | ssb_read32(mdev, SSB_INTVEC)));
105         } else {
106 +               u32 ipsflag = ssb_read32(mdev, SSB_IPSFLAG);
107 +               if ((ipsflag & ipsflag_irq_mask[irq]) != ipsflag_irq_mask[irq]) {
108 +                       u32 oldipsflag = (ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq];
109 +                       struct ssb_device *olddev = find_device(dev, oldipsflag);
110 +                       if (olddev)
111 +                               set_irq(olddev, 0);
112 +               }
113                 irqflag <<= ipsflag_irq_shift[irq];
114 -               irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]);
115 +               irqflag |= (ipsflag & ~ipsflag_irq_mask[irq]);
116                 ssb_write32(mdev, SSB_IPSFLAG, irqflag);
117         }
118 +       ssb_dprintk(KERN_INFO PFX
119 +                   "set_irq: core 0x%04x, irq %d => %d\n",
120 +                   dev->id.coreid, oldirq+2, irq+2);
121 +}
122 +
123 +static void print_irq(struct ssb_device *dev, unsigned int irq)
124 +{
125 +       int i;
126 +       static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
127 +       ssb_dprintk(KERN_INFO PFX
128 +               "core 0x%04x, irq :", dev->id.coreid);
129 +       for (i = 0; i <= 6; i++) {
130 +               ssb_dprintk(" %s%s", irq_name[i], i==irq?"*":" ");
131 +       }
132 +       ssb_dprintk("\n");
133 +}
134 +
135 +static void dump_irq(struct ssb_bus *bus)
136 +{
137 +       int i;
138 +       for (i = 0; i < bus->nr_devices; i++) {
139 +               struct ssb_device *dev;
140 +               dev = &(bus->devices[i]);
141 +               print_irq(dev, ssb_mips_irq(dev));
142 +       }
143  }
144  
145  static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
146 @@ -197,18 +253,26 @@ void ssb_mipscore_init(struct ssb_mipsco
147         else if (bus->chipco.dev)
148                 ssb_chipco_timing_init(&bus->chipco, ns);
149  
150 +       dump_irq(bus);
151         /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */
152         for (irq = 2, i = 0; i < bus->nr_devices; i++) {
153 +               int mips_irq;
154                 dev = &(bus->devices[i]);
155 -               dev->irq = ssb_mips_irq(dev) + 2;
156 +               mips_irq = ssb_mips_irq(dev);
157 +               if (mips_irq > 4)
158 +                       dev->irq = 0;
159 +               else
160 +                       dev->irq = mips_irq + 2;
161 +               if (dev->irq > 5)
162 +                       continue;
163                 switch (dev->id.coreid) {
164                 case SSB_DEV_USB11_HOST:
165                         /* shouldn't need a separate irq line for non-4710, most of them have a proper
166                          * external usb controller on the pci */
167                         if ((bus->chip_id == 0x4710) && (irq <= 4)) {
168                                 set_irq(dev, irq++);
169 -                               break;
170                         }
171 +                       break;
172                         /* fallthrough */
173                 case SSB_DEV_PCI:
174                 case SSB_DEV_ETHERNET:
175 @@ -222,6 +286,8 @@ void ssb_mipscore_init(struct ssb_mipsco
176                         }
177                 }
178         }
179 +       ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n");
180 +       dump_irq(bus);
181  
182         ssb_mips_serial_init(mcore);
183         ssb_mips_flash_detect(mcore);