[POWERPC] Make OF irq map code detect more error cases
Device-tree bugs on js20 with some versions of SLOF were causing the interrupt for IDE to not be parsed correctly and fail to boot. This patch adds a bit more sanity checking to the parser to detect some of those errors and fail instead of returning bogus information. The powerpc PCI code can then trigger a fallback that works on those machines. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Родитель
7233593b78
Коммит
006b64de60
|
@ -1289,6 +1289,9 @@ int pci_read_irq_line(struct pci_dev *pci_dev)
|
||||||
|
|
||||||
DBG("Try to map irq for %s...\n", pci_name(pci_dev));
|
DBG("Try to map irq for %s...\n", pci_name(pci_dev));
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
memset(&oirq, 0xff, sizeof(oirq));
|
||||||
|
#endif
|
||||||
/* Try to get a mapping from the device-tree */
|
/* Try to get a mapping from the device-tree */
|
||||||
if (of_irq_map_pci(pci_dev, &oirq)) {
|
if (of_irq_map_pci(pci_dev, &oirq)) {
|
||||||
u8 line, pin;
|
u8 line, pin;
|
||||||
|
@ -1314,8 +1317,9 @@ int pci_read_irq_line(struct pci_dev *pci_dev)
|
||||||
if (virq != NO_IRQ)
|
if (virq != NO_IRQ)
|
||||||
set_irq_type(virq, IRQ_TYPE_LEVEL_LOW);
|
set_irq_type(virq, IRQ_TYPE_LEVEL_LOW);
|
||||||
} else {
|
} else {
|
||||||
DBG(" -> got one, spec %d cells (0x%08x...) on %s\n",
|
DBG(" -> got one, spec %d cells (0x%08x 0x%08x...) on %s\n",
|
||||||
oirq.size, oirq.specifier[0], oirq.controller->full_name);
|
oirq.size, oirq.specifier[0], oirq.specifier[1],
|
||||||
|
oirq.controller->full_name);
|
||||||
|
|
||||||
virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
|
virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
|
||||||
oirq.size);
|
oirq.size);
|
||||||
|
@ -1324,6 +1328,9 @@ int pci_read_irq_line(struct pci_dev *pci_dev)
|
||||||
DBG(" -> failed to map !\n");
|
DBG(" -> failed to map !\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DBG(" -> mapped to linux irq %d\n", virq);
|
||||||
|
|
||||||
pci_dev->irq = virq;
|
pci_dev->irq = virq;
|
||||||
pci_write_config_byte(pci_dev, PCI_INTERRUPT_LINE, virq);
|
pci_write_config_byte(pci_dev, PCI_INTERRUPT_LINE, virq);
|
||||||
|
|
||||||
|
|
|
@ -639,14 +639,17 @@ void of_irq_map_init(unsigned int flags)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int of_irq_map_raw(struct device_node *parent, u32 *intspec, u32 *addr,
|
int of_irq_map_raw(struct device_node *parent, u32 *intspec, u32 ointsize,
|
||||||
struct of_irq *out_irq)
|
u32 *addr, struct of_irq *out_irq)
|
||||||
{
|
{
|
||||||
struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
|
struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
|
||||||
u32 *tmp, *imap, *imask;
|
u32 *tmp, *imap, *imask;
|
||||||
u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
|
u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
|
||||||
int imaplen, match, i;
|
int imaplen, match, i;
|
||||||
|
|
||||||
|
DBG("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n",
|
||||||
|
parent->full_name, intspec[0], intspec[1], ointsize);
|
||||||
|
|
||||||
ipar = of_node_get(parent);
|
ipar = of_node_get(parent);
|
||||||
|
|
||||||
/* First get the #interrupt-cells property of the current cursor
|
/* First get the #interrupt-cells property of the current cursor
|
||||||
|
@ -670,6 +673,9 @@ int of_irq_map_raw(struct device_node *parent, u32 *intspec, u32 *addr,
|
||||||
|
|
||||||
DBG("of_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize);
|
DBG("of_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize);
|
||||||
|
|
||||||
|
if (ointsize != intsize)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
/* Look for this #address-cells. We have to implement the old linux
|
/* Look for this #address-cells. We have to implement the old linux
|
||||||
* trick of looking for the parent here as some device-trees rely on it
|
* trick of looking for the parent here as some device-trees rely on it
|
||||||
*/
|
*/
|
||||||
|
@ -875,12 +881,15 @@ int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq
|
||||||
}
|
}
|
||||||
intsize = *tmp;
|
intsize = *tmp;
|
||||||
|
|
||||||
|
DBG(" intsize=%d intlen=%d\n", intsize, intlen);
|
||||||
|
|
||||||
/* Check index */
|
/* Check index */
|
||||||
if ((index + 1) * intsize > intlen)
|
if ((index + 1) * intsize > intlen)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Get new specifier and map it */
|
/* Get new specifier and map it */
|
||||||
res = of_irq_map_raw(p, intspec + index * intsize, addr, out_irq);
|
res = of_irq_map_raw(p, intspec + index * intsize, intsize,
|
||||||
|
addr, out_irq);
|
||||||
of_node_put(p);
|
of_node_put(p);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -965,7 +974,7 @@ int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq)
|
||||||
laddr[0] = (pdev->bus->number << 16)
|
laddr[0] = (pdev->bus->number << 16)
|
||||||
| (pdev->devfn << 8);
|
| (pdev->devfn << 8);
|
||||||
laddr[1] = laddr[2] = 0;
|
laddr[1] = laddr[2] = 0;
|
||||||
return of_irq_map_raw(ppnode, &lspec, laddr, out_irq);
|
return of_irq_map_raw(ppnode, &lspec, 1, laddr, out_irq);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_irq_map_pci);
|
EXPORT_SYMBOL_GPL(of_irq_map_pci);
|
||||||
#endif /* CONFIG_PCI */
|
#endif /* CONFIG_PCI */
|
||||||
|
|
|
@ -276,6 +276,7 @@ extern void of_irq_map_init(unsigned int flags);
|
||||||
* of_irq_map_raw - Low level interrupt tree parsing
|
* of_irq_map_raw - Low level interrupt tree parsing
|
||||||
* @parent: the device interrupt parent
|
* @parent: the device interrupt parent
|
||||||
* @intspec: interrupt specifier ("interrupts" property of the device)
|
* @intspec: interrupt specifier ("interrupts" property of the device)
|
||||||
|
* @ointsize: size of the passed in interrupt specifier
|
||||||
* @addr: address specifier (start of "reg" property of the device)
|
* @addr: address specifier (start of "reg" property of the device)
|
||||||
* @out_irq: structure of_irq filled by this function
|
* @out_irq: structure of_irq filled by this function
|
||||||
*
|
*
|
||||||
|
@ -288,7 +289,8 @@ extern void of_irq_map_init(unsigned int flags);
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern int of_irq_map_raw(struct device_node *parent, u32 *intspec, u32 *addr,
|
extern int of_irq_map_raw(struct device_node *parent, u32 *intspec,
|
||||||
|
u32 ointsize, u32 *addr,
|
||||||
struct of_irq *out_irq);
|
struct of_irq *out_irq);
|
||||||
|
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче