| 1 | From b2ee3bd8706521c9bbf43405c767010927c101e5 Mon Sep 17 00:00:00 2001 |
| 2 | From: Gabor Juhos <juhosg@openwrt.org> |
| 3 | Date: Mon, 21 Nov 2011 17:57:51 +0100 |
| 4 | Subject: [PATCH 11/35] MIPS: ath79: add a workaround for a PCI controller bug in AR7240 SoCs |
| 5 | |
| 6 | The PCI controller of the AR724X SoCs has a hardware |
| 7 | bag. If the BAR0 register of the PCI device is set to |
| 8 | the proper base address, the memory address space of |
| 9 | the device is not accessible. |
| 10 | |
| 11 | When the device driver tries to access the memory |
| 12 | address space of the PCI device, it leads to data |
| 13 | bus error, similiar to this: |
| 14 | |
| 15 | Data bus error, epc == 801f69a0, ra == 801f698c |
| 16 | Oops[#1]: |
| 17 | Cpu 0 |
| 18 | $ 0 : 00000000 00000061 deadbeef 000000ff |
| 19 | $ 4 : 00000000 000000ff 00000014 00000000 |
| 20 | $ 8 : ff000000 fffffffc 00000000 00000000 |
| 21 | $12 : 000001f5 00000006 00000000 6e637920 |
| 22 | $16 : 81ca4000 81ca0260 81ca4000 804d70f0 |
| 23 | $20 : fffffff4 0000002b 803ad4c4 00000000 |
| 24 | $24 : 00000003 00000000 |
| 25 | $28 : 81c20000 81c21c60 00000000 801f698c |
| 26 | Hi : 00000000 |
| 27 | Lo : 00000000 |
| 28 | epc : 801f69a0 ath9k_hw_init+0xd0/0xa70 |
| 29 | Not tainted |
| 30 | ra : 801f698c ath9k_hw_init+0xbc/0xa70 |
| 31 | Status: 1000c103 KERNEL EXL IE |
| 32 | Cause : 1080001c |
| 33 | PrId : 00019374 (MIPS 24Kc) |
| 34 | Modules linked in: |
| 35 | Process swapper (pid: 1, threadinfo=81c20000, task=81c18000, tls=00000000) |
| 36 | Stack : 00000000 00000000 00000000 00000000 81c21c78 81ca0260 00000000 804d70f0 |
| 37 | 81ca0260 81c21cc0 81ca0e80 81ca0260 81ca4000 804d70f0 fffffff4 0000002b |
| 38 | 803ad4c4 00000000 00000000 801e3ae8 81c9d080 81ca0e80 b0000000 800b9b9c |
| 39 | 00000008 81c9d000 8031aeb0 802d38a0 00000000 81c14c00 81c14c60 00000000 |
| 40 | 81ca0e80 81ca0260 b0000000 801f08a4 81c9c820 81c21d48 81c9c820 80144320 |
| 41 | ... |
| 42 | Call Trace: |
| 43 | [<801f69a0>] ath9k_hw_init+0xd0/0xa70 |
| 44 | [<801e3ae8>] ath9k_init_device+0x174/0x680 |
| 45 | [<801f08a4>] ath_pci_probe+0x27c/0x380 |
| 46 | [<8019e490>] pci_device_probe+0x74/0x9c |
| 47 | [<801bfadc>] driver_probe_device+0x9c/0x1b4 |
| 48 | [<801bfcb0>] __driver_attach+0xbc/0xc4 |
| 49 | [<801bea0c>] bus_for_each_dev+0x5c/0x98 |
| 50 | [<801bf394>] bus_add_driver+0x1d0/0x2a4 |
| 51 | [<801c0364>] driver_register+0x8c/0x16c |
| 52 | [<8019e72c>] __pci_register_driver+0x4c/0xe4 |
| 53 | [<803d3d40>] ath9k_init+0x3c/0x88 |
| 54 | [<80060930>] do_one_initcall+0x3c/0x1cc |
| 55 | [<803c297c>] kernel_init+0xa4/0x138 |
| 56 | [<80063c04>] kernel_thread_helper+0x10/0x18 |
| 57 | |
| 58 | Signed-off-by: Gabor Juhos <juhosg@openwrt.org> |
| 59 | |
| 60 | v2: - apply the workaround on AR7240 only |
| 61 | - remove unrelated defines |
| 62 | --- |
| 63 | arch/mips/pci/pci-ar724x.c | 36 +++++++++++++++++++++++++++++++++++- |
| 64 | 1 files changed, 35 insertions(+), 1 deletions(-) |
| 65 | |
| 66 | --- a/arch/mips/pci/pci-ar724x.c |
| 67 | +++ b/arch/mips/pci/pci-ar724x.c |
| 68 | @@ -9,6 +9,7 @@ |
| 69 | */ |
| 70 | |
| 71 | #include <linux/pci.h> |
| 72 | +#include <asm/mach-ath79/ath79.h> |
| 73 | #include <asm/mach-ath79/pci.h> |
| 74 | |
| 75 | #define AR724X_PCI_CFG_BASE 0x14000000 |
| 76 | @@ -16,9 +17,14 @@ |
| 77 | #define AR724X_PCI_MEM_BASE 0x10000000 |
| 78 | #define AR724X_PCI_MEM_SIZE 0x08000000 |
| 79 | |
| 80 | +#define AR7240_BAR0_WAR_VALUE 0xffff |
| 81 | + |
| 82 | static DEFINE_SPINLOCK(ar724x_pci_lock); |
| 83 | static void __iomem *ar724x_pci_devcfg_base; |
| 84 | |
| 85 | +static u32 ar724x_pci_bar0_value; |
| 86 | +static bool ar724x_pci_bar0_is_cached; |
| 87 | + |
| 88 | static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, |
| 89 | int size, uint32_t *value) |
| 90 | { |
| 91 | @@ -56,7 +62,14 @@ static int ar724x_pci_read(struct pci_bu |
| 92 | } |
| 93 | |
| 94 | spin_unlock_irqrestore(&ar724x_pci_lock, flags); |
| 95 | - *value = data; |
| 96 | + |
| 97 | + if (where == PCI_BASE_ADDRESS_0 && size == 4 && |
| 98 | + ar724x_pci_bar0_is_cached) { |
| 99 | + /* use the cached value */ |
| 100 | + *value = ar724x_pci_bar0_value; |
| 101 | + } else { |
| 102 | + *value = data; |
| 103 | + } |
| 104 | |
| 105 | return PCIBIOS_SUCCESSFUL; |
| 106 | } |
| 107 | @@ -72,6 +85,27 @@ static int ar724x_pci_write(struct pci_b |
| 108 | if (devfn) |
| 109 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 110 | |
| 111 | + if (soc_is_ar7240() && where == PCI_BASE_ADDRESS_0 && size == 4) { |
| 112 | + if (value != 0xffffffff) { |
| 113 | + /* |
| 114 | + * WAR for a hw issue. If the BAR0 register of the |
| 115 | + * device is set to the proper base address, the |
| 116 | + * memory space of the device is not accessible. |
| 117 | + * |
| 118 | + * Cache the intended value so it can be read back, |
| 119 | + * and write a SoC specific constant value to the |
| 120 | + * BAR0 register in order to make the device memory |
| 121 | + * accessible. |
| 122 | + */ |
| 123 | + ar724x_pci_bar0_is_cached = true; |
| 124 | + ar724x_pci_bar0_value = value; |
| 125 | + |
| 126 | + value = AR7240_BAR0_WAR_VALUE; |
| 127 | + } else { |
| 128 | + ar724x_pci_bar0_is_cached = false; |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | base = ar724x_pci_devcfg_base; |
| 133 | |
| 134 | spin_lock_irqsave(&ar724x_pci_lock, flags); |
| 135 | |