linux/arch/mips/ath79/pci.c

274 lines
6.3 KiB
C
Raw Normal View History

/*
* Atheros AR71XX/AR724X specific PCI setup code
*
* Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/resource.h>
#include <linux/platform_device.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/irq.h>
#include "pci.h"
MIPS: ath79: allow to use board specific pci_plat_dev_init functions Th current implementation causes NULL pointer dereference if 'pci_data' is not set: pci 0000:00:00.0: BAR 0: assigned [mem 0x10000000-0x1000ffff 64bit] pci 0000:00:00.0: BAR 0: set to [mem 0x10000000-0x1000ffff 64bit] (PCI address [0x10000000-0x1000ffff]) CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 802daca0, ra == 802e78a4 Oops[#1]: Cpu 0 $ 0 : 00000000 80420000 00000000 00000000 $ 4 : 00000000 00000000 00000001 00000001 $ 8 : 00000001 0000032c 81c54700 00000001 $12 : 0000032d 0000000f 00000000 ffffffff $16 : 81c14c00 00000001 802dac74 80195f98 $20 : 802ea050 00000000 00000000 00000000 $24 : 00000003 800617f0 $28 : 81c20000 81c21e70 00000000 802e78a4 Hi : 00000000 Lo : 4190ab00 epc : 802daca0 0x802daca0 Not tainted ra : 802e78a4 0x802e78a4 Status: 1000c003 KERNEL EXL IE Cause : 00800008 BadVA : 00000000 PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 8027d5d8 802e8ae0 00000000 01000000 802e8b5c 81c50600 00000000 802ff290 00000000 80420000 802ea0bc 00000000 00000000 80420000 802ff290 80420000 80060930 33390000 00000000 00002308 80140a80 00000028 802d0000 00000000 800ba024 802ff004 802ff0c8 802ff290 00000000 00000000 00000000 00000000 802d897c 01234567 7f827068 00000000 0045f798 00460000 00000000 This can be avoided by calling the 'ar724x_pci_add_data' function from the board specific setup code. However it makes no sense to use that function for every board, especially when the board does not needs to set the platform_data field of any PCI device. The patch allows the board setup code to specify a board specific function if that is required. Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3499/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:09 +08:00
static int (*ath79_pci_plat_dev_init)(struct pci_dev *dev);
static const struct ath79_pci_irq *ath79_pci_irq_map;
static unsigned ath79_pci_nr_irqs;
static const struct ath79_pci_irq ar71xx_pci_irq_map[] = {
{
.slot = 17,
.pin = 1,
.irq = ATH79_PCI_IRQ(0),
}, {
.slot = 18,
.pin = 1,
.irq = ATH79_PCI_IRQ(1),
}, {
.slot = 19,
.pin = 1,
.irq = ATH79_PCI_IRQ(2),
}
};
static const struct ath79_pci_irq ar724x_pci_irq_map[] = {
{
.slot = 0,
.pin = 1,
.irq = ATH79_PCI_IRQ(0),
}
};
static const struct ath79_pci_irq qca955x_pci_irq_map[] = {
{
.bus = 0,
.slot = 0,
.pin = 1,
.irq = ATH79_PCI_IRQ(0),
},
{
.bus = 1,
.slot = 0,
.pin = 1,
.irq = ATH79_PCI_IRQ(1),
},
};
int pcibios_map_irq(const struct pci_dev *dev, uint8_t slot, uint8_t pin)
{
int irq = -1;
int i;
if (ath79_pci_nr_irqs == 0 ||
ath79_pci_irq_map == NULL) {
if (soc_is_ar71xx()) {
ath79_pci_irq_map = ar71xx_pci_irq_map;
ath79_pci_nr_irqs = ARRAY_SIZE(ar71xx_pci_irq_map);
} else if (soc_is_ar724x() ||
soc_is_ar9342() ||
soc_is_ar9344()) {
ath79_pci_irq_map = ar724x_pci_irq_map;
ath79_pci_nr_irqs = ARRAY_SIZE(ar724x_pci_irq_map);
} else if (soc_is_qca955x()) {
ath79_pci_irq_map = qca955x_pci_irq_map;
ath79_pci_nr_irqs = ARRAY_SIZE(qca955x_pci_irq_map);
} else {
pr_crit("pci %s: invalid irq map\n",
pci_name((struct pci_dev *) dev));
return irq;
}
}
for (i = 0; i < ath79_pci_nr_irqs; i++) {
const struct ath79_pci_irq *entry;
entry = &ath79_pci_irq_map[i];
if (entry->bus == dev->bus->number &&
entry->slot == slot &&
entry->pin == pin) {
irq = entry->irq;
break;
}
}
if (irq < 0)
pr_crit("pci %s: no irq found for pin %u\n",
pci_name((struct pci_dev *) dev), pin);
else
pr_info("pci %s: using irq %d for pin %u\n",
pci_name((struct pci_dev *) dev), irq, pin);
return irq;
}
int pcibios_plat_dev_init(struct pci_dev *dev)
{
MIPS: ath79: allow to use board specific pci_plat_dev_init functions Th current implementation causes NULL pointer dereference if 'pci_data' is not set: pci 0000:00:00.0: BAR 0: assigned [mem 0x10000000-0x1000ffff 64bit] pci 0000:00:00.0: BAR 0: set to [mem 0x10000000-0x1000ffff 64bit] (PCI address [0x10000000-0x1000ffff]) CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 802daca0, ra == 802e78a4 Oops[#1]: Cpu 0 $ 0 : 00000000 80420000 00000000 00000000 $ 4 : 00000000 00000000 00000001 00000001 $ 8 : 00000001 0000032c 81c54700 00000001 $12 : 0000032d 0000000f 00000000 ffffffff $16 : 81c14c00 00000001 802dac74 80195f98 $20 : 802ea050 00000000 00000000 00000000 $24 : 00000003 800617f0 $28 : 81c20000 81c21e70 00000000 802e78a4 Hi : 00000000 Lo : 4190ab00 epc : 802daca0 0x802daca0 Not tainted ra : 802e78a4 0x802e78a4 Status: 1000c003 KERNEL EXL IE Cause : 00800008 BadVA : 00000000 PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 8027d5d8 802e8ae0 00000000 01000000 802e8b5c 81c50600 00000000 802ff290 00000000 80420000 802ea0bc 00000000 00000000 80420000 802ff290 80420000 80060930 33390000 00000000 00002308 80140a80 00000028 802d0000 00000000 800ba024 802ff004 802ff0c8 802ff290 00000000 00000000 00000000 00000000 802d897c 01234567 7f827068 00000000 0045f798 00460000 00000000 This can be avoided by calling the 'ar724x_pci_add_data' function from the board specific setup code. However it makes no sense to use that function for every board, especially when the board does not needs to set the platform_data field of any PCI device. The patch allows the board setup code to specify a board specific function if that is required. Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3499/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:09 +08:00
if (ath79_pci_plat_dev_init)
return ath79_pci_plat_dev_init(dev);
MIPS: ath79: allow to use board specific pci_plat_dev_init functions Th current implementation causes NULL pointer dereference if 'pci_data' is not set: pci 0000:00:00.0: BAR 0: assigned [mem 0x10000000-0x1000ffff 64bit] pci 0000:00:00.0: BAR 0: set to [mem 0x10000000-0x1000ffff 64bit] (PCI address [0x10000000-0x1000ffff]) CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 802daca0, ra == 802e78a4 Oops[#1]: Cpu 0 $ 0 : 00000000 80420000 00000000 00000000 $ 4 : 00000000 00000000 00000001 00000001 $ 8 : 00000001 0000032c 81c54700 00000001 $12 : 0000032d 0000000f 00000000 ffffffff $16 : 81c14c00 00000001 802dac74 80195f98 $20 : 802ea050 00000000 00000000 00000000 $24 : 00000003 800617f0 $28 : 81c20000 81c21e70 00000000 802e78a4 Hi : 00000000 Lo : 4190ab00 epc : 802daca0 0x802daca0 Not tainted ra : 802e78a4 0x802e78a4 Status: 1000c003 KERNEL EXL IE Cause : 00800008 BadVA : 00000000 PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 8027d5d8 802e8ae0 00000000 01000000 802e8b5c 81c50600 00000000 802ff290 00000000 80420000 802ea0bc 00000000 00000000 80420000 802ff290 80420000 80060930 33390000 00000000 00002308 80140a80 00000028 802d0000 00000000 800ba024 802ff004 802ff0c8 802ff290 00000000 00000000 00000000 00000000 802d897c 01234567 7f827068 00000000 0045f798 00460000 00000000 This can be avoided by calling the 'ar724x_pci_add_data' function from the board specific setup code. However it makes no sense to use that function for every board, especially when the board does not needs to set the platform_data field of any PCI device. The patch allows the board setup code to specify a board specific function if that is required. Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3499/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:09 +08:00
return 0;
}
void __init ath79_pci_set_irq_map(unsigned nr_irqs,
const struct ath79_pci_irq *map)
{
ath79_pci_nr_irqs = nr_irqs;
ath79_pci_irq_map = map;
}
MIPS: ath79: allow to use board specific pci_plat_dev_init functions Th current implementation causes NULL pointer dereference if 'pci_data' is not set: pci 0000:00:00.0: BAR 0: assigned [mem 0x10000000-0x1000ffff 64bit] pci 0000:00:00.0: BAR 0: set to [mem 0x10000000-0x1000ffff 64bit] (PCI address [0x10000000-0x1000ffff]) CPU 0 Unable to handle kernel paging request at virtual address 00000000, epc == 802daca0, ra == 802e78a4 Oops[#1]: Cpu 0 $ 0 : 00000000 80420000 00000000 00000000 $ 4 : 00000000 00000000 00000001 00000001 $ 8 : 00000001 0000032c 81c54700 00000001 $12 : 0000032d 0000000f 00000000 ffffffff $16 : 81c14c00 00000001 802dac74 80195f98 $20 : 802ea050 00000000 00000000 00000000 $24 : 00000003 800617f0 $28 : 81c20000 81c21e70 00000000 802e78a4 Hi : 00000000 Lo : 4190ab00 epc : 802daca0 0x802daca0 Not tainted ra : 802e78a4 0x802e78a4 Status: 1000c003 KERNEL EXL IE Cause : 00800008 BadVA : 00000000 PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 8027d5d8 802e8ae0 00000000 01000000 802e8b5c 81c50600 00000000 802ff290 00000000 80420000 802ea0bc 00000000 00000000 80420000 802ff290 80420000 80060930 33390000 00000000 00002308 80140a80 00000028 802d0000 00000000 800ba024 802ff004 802ff0c8 802ff290 00000000 00000000 00000000 00000000 802d897c 01234567 7f827068 00000000 0045f798 00460000 00000000 This can be avoided by calling the 'ar724x_pci_add_data' function from the board specific setup code. However it makes no sense to use that function for every board, especially when the board does not needs to set the platform_data field of any PCI device. The patch allows the board setup code to specify a board specific function if that is required. Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3499/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:09 +08:00
void __init ath79_pci_set_plat_dev_init(int (*func)(struct pci_dev *dev))
{
ath79_pci_plat_dev_init = func;
}
static struct platform_device *
ath79_register_pci_ar71xx(void)
{
struct platform_device *pdev;
struct resource res[4];
memset(res, 0, sizeof(res));
res[0].name = "cfg_base";
res[0].flags = IORESOURCE_MEM;
res[0].start = AR71XX_PCI_CFG_BASE;
res[0].end = AR71XX_PCI_CFG_BASE + AR71XX_PCI_CFG_SIZE - 1;
res[1].flags = IORESOURCE_IRQ;
res[1].start = ATH79_CPU_IRQ(2);
res[1].end = ATH79_CPU_IRQ(2);
res[2].name = "io_base";
res[2].flags = IORESOURCE_IO;
res[2].start = 0;
res[2].end = 0;
res[3].name = "mem_base";
res[3].flags = IORESOURCE_MEM;
res[3].start = AR71XX_PCI_MEM_BASE;
res[3].end = AR71XX_PCI_MEM_BASE + AR71XX_PCI_MEM_SIZE - 1;
pdev = platform_device_register_simple("ar71xx-pci", -1,
res, ARRAY_SIZE(res));
return pdev;
}
static struct platform_device *
ath79_register_pci_ar724x(int id,
unsigned long cfg_base,
unsigned long ctrl_base,
unsigned long crp_base,
unsigned long mem_base,
unsigned long mem_size,
unsigned long io_base,
int irq)
{
struct platform_device *pdev;
struct resource res[6];
memset(res, 0, sizeof(res));
res[0].name = "cfg_base";
res[0].flags = IORESOURCE_MEM;
res[0].start = cfg_base;
res[0].end = cfg_base + AR724X_PCI_CFG_SIZE - 1;
res[1].name = "ctrl_base";
res[1].flags = IORESOURCE_MEM;
res[1].start = ctrl_base;
res[1].end = ctrl_base + AR724X_PCI_CTRL_SIZE - 1;
res[2].flags = IORESOURCE_IRQ;
res[2].start = irq;
res[2].end = irq;
res[3].name = "mem_base";
res[3].flags = IORESOURCE_MEM;
res[3].start = mem_base;
res[3].end = mem_base + mem_size - 1;
res[4].name = "io_base";
res[4].flags = IORESOURCE_IO;
res[4].start = io_base;
res[4].end = io_base;
res[5].name = "crp_base";
res[5].flags = IORESOURCE_MEM;
res[5].start = crp_base;
res[5].end = crp_base + AR724X_PCI_CRP_SIZE - 1;
pdev = platform_device_register_simple("ar724x-pci", id,
res, ARRAY_SIZE(res));
return pdev;
}
int __init ath79_register_pci(void)
{
struct platform_device *pdev = NULL;
if (soc_is_ar71xx()) {
pdev = ath79_register_pci_ar71xx();
} else if (soc_is_ar724x()) {
pdev = ath79_register_pci_ar724x(-1,
AR724X_PCI_CFG_BASE,
AR724X_PCI_CTRL_BASE,
AR724X_PCI_CRP_BASE,
AR724X_PCI_MEM_BASE,
AR724X_PCI_MEM_SIZE,
0,
ATH79_CPU_IRQ(2));
} else if (soc_is_ar9342() ||
soc_is_ar9344()) {
u32 bootstrap;
bootstrap = ath79_reset_rr(AR934X_RESET_REG_BOOTSTRAP);
if ((bootstrap & AR934X_BOOTSTRAP_PCIE_RC) == 0)
return -ENODEV;
pdev = ath79_register_pci_ar724x(-1,
AR724X_PCI_CFG_BASE,
AR724X_PCI_CTRL_BASE,
AR724X_PCI_CRP_BASE,
AR724X_PCI_MEM_BASE,
AR724X_PCI_MEM_SIZE,
0,
ATH79_IP2_IRQ(0));
} else if (soc_is_qca9558()) {
pdev = ath79_register_pci_ar724x(0,
QCA955X_PCI_CFG_BASE0,
QCA955X_PCI_CTRL_BASE0,
QCA955X_PCI_CRP_BASE0,
QCA955X_PCI_MEM_BASE0,
QCA955X_PCI_MEM_SIZE,
0,
ATH79_IP2_IRQ(0));
pdev = ath79_register_pci_ar724x(1,
QCA955X_PCI_CFG_BASE1,
QCA955X_PCI_CTRL_BASE1,
QCA955X_PCI_CRP_BASE1,
QCA955X_PCI_MEM_BASE1,
QCA955X_PCI_MEM_SIZE,
1,
ATH79_IP3_IRQ(2));
} else {
/* No PCI support */
return -ENODEV;
}
if (!pdev)
pr_err("unable to register PCI controller device\n");
return pdev ? 0 : -ENODEV;
}