linux/arch/mips/pci/pci-ar724x.c

412 lines
9.2 KiB
C
Raw Normal View History

/*
* Atheros AR724X PCI host controller driver
*
* Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com>
* Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
*
* 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/irq.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/platform_device.h>
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#define AR724X_PCI_REG_RESET 0x18
#define AR724X_PCI_REG_INT_STATUS 0x4c
#define AR724X_PCI_REG_INT_MASK 0x50
#define AR724X_PCI_RESET_LINK_UP BIT(0)
#define AR724X_PCI_INT_DEV0 BIT(14)
#define AR724X_PCI_IRQ_COUNT 1
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
#define AR7240_BAR0_WAR_VALUE 0xffff
#define AR724X_PCI_CMD_INIT (PCI_COMMAND_MEMORY | \
PCI_COMMAND_MASTER | \
PCI_COMMAND_INVALIDATE | \
PCI_COMMAND_PARITY | \
PCI_COMMAND_SERR | \
PCI_COMMAND_FAST_BACK)
struct ar724x_pci_controller {
void __iomem *devcfg_base;
void __iomem *ctrl_base;
void __iomem *crp_base;
int irq;
int irq_base;
bool link_up;
bool bar0_is_cached;
u32 bar0_value;
struct pci_controller pci_controller;
struct resource io_res;
struct resource mem_res;
};
static inline bool ar724x_pci_check_link(struct ar724x_pci_controller *apc)
{
u32 reset;
reset = __raw_readl(apc->ctrl_base + AR724X_PCI_REG_RESET);
return reset & AR724X_PCI_RESET_LINK_UP;
}
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
static inline struct ar724x_pci_controller *
pci_bus_to_ar724x_controller(struct pci_bus *bus)
{
struct pci_controller *hose;
hose = (struct pci_controller *) bus->sysdata;
return container_of(hose, struct ar724x_pci_controller, pci_controller);
}
static int ar724x_pci_local_write(struct ar724x_pci_controller *apc,
int where, int size, u32 value)
{
void __iomem *base;
u32 data;
int s;
WARN_ON(where & (size - 1));
if (!apc->link_up)
return PCIBIOS_DEVICE_NOT_FOUND;
base = apc->crp_base;
data = __raw_readl(base + (where & ~3));
switch (size) {
case 1:
s = ((where & 3) * 8);
data &= ~(0xff << s);
data |= ((value & 0xff) << s);
break;
case 2:
s = ((where & 2) * 8);
data &= ~(0xffff << s);
data |= ((value & 0xffff) << s);
break;
case 4:
data = value;
break;
default:
return PCIBIOS_BAD_REGISTER_NUMBER;
}
__raw_writel(data, base + (where & ~3));
/* flush write */
__raw_readl(base + (where & ~3));
return PCIBIOS_SUCCESSFUL;
}
static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where,
int size, uint32_t *value)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
u32 data;
apc = pci_bus_to_ar724x_controller(bus);
if (!apc->link_up)
return PCIBIOS_DEVICE_NOT_FOUND;
if (devfn)
return PCIBIOS_DEVICE_NOT_FOUND;
base = apc->devcfg_base;
data = __raw_readl(base + (where & ~3));
switch (size) {
case 1:
if (where & 1)
data >>= 8;
if (where & 2)
data >>= 16;
data &= 0xff;
break;
case 2:
if (where & 2)
data >>= 16;
data &= 0xffff;
break;
case 4:
break;
default:
return PCIBIOS_BAD_REGISTER_NUMBER;
}
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
if (where == PCI_BASE_ADDRESS_0 && size == 4 &&
apc->bar0_is_cached) {
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
/* use the cached value */
*value = apc->bar0_value;
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
} else {
*value = data;
}
return PCIBIOS_SUCCESSFUL;
}
static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where,
int size, uint32_t value)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
u32 data;
int s;
apc = pci_bus_to_ar724x_controller(bus);
if (!apc->link_up)
return PCIBIOS_DEVICE_NOT_FOUND;
if (devfn)
return PCIBIOS_DEVICE_NOT_FOUND;
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
if (soc_is_ar7240() && where == PCI_BASE_ADDRESS_0 && size == 4) {
if (value != 0xffffffff) {
/*
* WAR for a hw issue. If the BAR0 register of the
* device is set to the proper base address, the
* memory space of the device is not accessible.
*
* Cache the intended value so it can be read back,
* and write a SoC specific constant value to the
* BAR0 register in order to make the device memory
* accessible.
*/
apc->bar0_is_cached = true;
apc->bar0_value = value;
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
value = AR7240_BAR0_WAR_VALUE;
} else {
apc->bar0_is_cached = false;
MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs The PCI controller of the AR724X SoCs has a hardware bag. If the BAR0 register of the PCI device is set to the proper base address, the memory address space of the device is not accessible. When the device driver tries to access the memory address space of the PCI device, it leads to data bus error, similiar to this: Data bus error, epc == 801f69a0, ra == 801f698c Oops[#1]: Cpu 0 $ 0 : 00000000 00000061 deadbeef 000000ff $ 4 : 00000000 000000ff 00000014 00000000 $ 8 : ff000000 fffffffc 00000000 00000000 $12 : 000001f5 00000006 00000000 6e637920 $16 : 81ca4000 81ca0260 81ca4000 804d70f0 $20 : fffffff4 0000002b 803ad4c4 00000000 $24 : 00000003 00000000 $28 : 81c20000 81c21c60 00000000 801f698c Hi : 00000000 Lo : 00000000 epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 Not tainted ra : 801f698c ath9k_hw_init+0xbc/0xa70 Status: 1000c103 KERNEL EXL IE Cause : 1080001c PrId : 00019374 (MIPS 24Kc) Modules linked in: Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 ... Call Trace: [<801f69a0>] ath9k_hw_init+0xd0/0xa70 [<801e3ae8>] ath9k_init_device+0x174/0x680 [<801f08a4>] ath_pci_probe+0x27c/0x380 [<8019e490>] pci_device_probe+0x74/0x9c [<801bfadc>] driver_probe_device+0x9c/0x1b4 [<801bfcb0>] __driver_attach+0xbc/0xc4 [<801bea0c>] bus_for_each_dev+0x5c/0x98 [<801bf394>] bus_add_driver+0x1d0/0x2a4 [<801c0364>] driver_register+0x8c/0x16c [<8019e72c>] __pci_register_driver+0x4c/0xe4 [<803d3d40>] ath9k_init+0x3c/0x88 [<80060930>] do_one_initcall+0x3c/0x1cc [<803c297c>] kernel_init+0xa4/0x138 [<80063c04>] kernel_thread_helper+0x10/0x18 Signed-off-by: Gabor Juhos <juhosg@openwrt.org> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/3494/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2012-03-14 17:36:05 +08:00
}
}
base = apc->devcfg_base;
data = __raw_readl(base + (where & ~3));
switch (size) {
case 1:
s = ((where & 3) * 8);
data &= ~(0xff << s);
data |= ((value & 0xff) << s);
break;
case 2:
s = ((where & 2) * 8);
data &= ~(0xffff << s);
data |= ((value & 0xffff) << s);
break;
case 4:
data = value;
break;
default:
return PCIBIOS_BAD_REGISTER_NUMBER;
}
__raw_writel(data, base + (where & ~3));
/* flush write */
__raw_readl(base + (where & ~3));
return PCIBIOS_SUCCESSFUL;
}
static struct pci_ops ar724x_pci_ops = {
.read = ar724x_pci_read,
.write = ar724x_pci_write,
};
static void ar724x_pci_irq_handler(struct irq_desc *desc)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
u32 pending;
apc = irq_desc_get_handler_data(desc);
base = apc->ctrl_base;
pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) &
__raw_readl(base + AR724X_PCI_REG_INT_MASK);
if (pending & AR724X_PCI_INT_DEV0)
generic_handle_irq(apc->irq_base + 0);
else
spurious_interrupt();
}
static void ar724x_pci_irq_unmask(struct irq_data *d)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
int offset;
u32 t;
apc = irq_data_get_irq_chip_data(d);
base = apc->ctrl_base;
offset = apc->irq_base - d->irq;
switch (offset) {
case 0:
t = __raw_readl(base + AR724X_PCI_REG_INT_MASK);
__raw_writel(t | AR724X_PCI_INT_DEV0,
base + AR724X_PCI_REG_INT_MASK);
/* flush write */
__raw_readl(base + AR724X_PCI_REG_INT_MASK);
}
}
static void ar724x_pci_irq_mask(struct irq_data *d)
{
struct ar724x_pci_controller *apc;
void __iomem *base;
int offset;
u32 t;
apc = irq_data_get_irq_chip_data(d);
base = apc->ctrl_base;
offset = apc->irq_base - d->irq;
switch (offset) {
case 0:
t = __raw_readl(base + AR724X_PCI_REG_INT_MASK);
__raw_writel(t & ~AR724X_PCI_INT_DEV0,
base + AR724X_PCI_REG_INT_MASK);
/* flush write */
__raw_readl(base + AR724X_PCI_REG_INT_MASK);
t = __raw_readl(base + AR724X_PCI_REG_INT_STATUS);
__raw_writel(t | AR724X_PCI_INT_DEV0,
base + AR724X_PCI_REG_INT_STATUS);
/* flush write */
__raw_readl(base + AR724X_PCI_REG_INT_STATUS);
}
}
static struct irq_chip ar724x_pci_irq_chip = {
.name = "AR724X PCI ",
.irq_mask = ar724x_pci_irq_mask,
.irq_unmask = ar724x_pci_irq_unmask,
.irq_mask_ack = ar724x_pci_irq_mask,
};
static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc,
int id)
{
void __iomem *base;
int i;
base = apc->ctrl_base;
__raw_writel(0, base + AR724X_PCI_REG_INT_MASK);
__raw_writel(0, base + AR724X_PCI_REG_INT_STATUS);
apc->irq_base = ATH79_PCI_IRQ_BASE + (id * AR724X_PCI_IRQ_COUNT);
for (i = apc->irq_base;
i < apc->irq_base + AR724X_PCI_IRQ_COUNT; i++) {
irq_set_chip_and_handler(i, &ar724x_pci_irq_chip,
handle_level_irq);
irq_set_chip_data(i, apc);
}
irq_set_chained_handler_and_data(apc->irq, ar724x_pci_irq_handler,
apc);
}
static int ar724x_pci_probe(struct platform_device *pdev)
{
struct ar724x_pci_controller *apc;
struct resource *res;
int id;
id = pdev->id;
if (id == -1)
id = 0;
apc = devm_kzalloc(&pdev->dev, sizeof(struct ar724x_pci_controller),
GFP_KERNEL);
if (!apc)
return -ENOMEM;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl_base");
apc->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(apc->ctrl_base))
return PTR_ERR(apc->ctrl_base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base");
apc->devcfg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(apc->devcfg_base))
return PTR_ERR(apc->devcfg_base);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crp_base");
apc->crp_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(apc->crp_base))
return PTR_ERR(apc->crp_base);
apc->irq = platform_get_irq(pdev, 0);
if (apc->irq < 0)
return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base");
if (!res)
return -EINVAL;
apc->io_res.parent = res;
apc->io_res.name = "PCI IO space";
apc->io_res.start = res->start;
apc->io_res.end = res->end;
apc->io_res.flags = IORESOURCE_IO;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base");
if (!res)
return -EINVAL;
apc->mem_res.parent = res;
apc->mem_res.name = "PCI memory space";
apc->mem_res.start = res->start;
apc->mem_res.end = res->end;
apc->mem_res.flags = IORESOURCE_MEM;
apc->pci_controller.pci_ops = &ar724x_pci_ops;
apc->pci_controller.io_resource = &apc->io_res;
apc->pci_controller.mem_resource = &apc->mem_res;
apc->link_up = ar724x_pci_check_link(apc);
if (!apc->link_up)
dev_warn(&pdev->dev, "PCIe link is down\n");
ar724x_pci_irq_init(apc, id);
ar724x_pci_local_write(apc, PCI_COMMAND, 4, AR724X_PCI_CMD_INIT);
register_pci_controller(&apc->pci_controller);
return 0;
}
static struct platform_driver ar724x_pci_driver = {
.probe = ar724x_pci_probe,
.driver = {
.name = "ar724x-pci",
},
};
static int __init ar724x_pci_init(void)
{
return platform_driver_register(&ar724x_pci_driver);
}
postcore_initcall(ar724x_pci_init);