135 lines
4.2 KiB
Diff
135 lines
4.2 KiB
Diff
From b2ee3bd8706521c9bbf43405c767010927c101e5 Mon Sep 17 00:00:00 2001
|
|
From: Gabor Juhos <juhosg@openwrt.org>
|
|
Date: Mon, 21 Nov 2011 17:57:51 +0100
|
|
Subject: [PATCH 11/35] 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>
|
|
|
|
v2: - apply the workaround on AR7240 only
|
|
- remove unrelated defines
|
|
---
|
|
arch/mips/pci/pci-ar724x.c | 36 +++++++++++++++++++++++++++++++++++-
|
|
1 files changed, 35 insertions(+), 1 deletions(-)
|
|
|
|
--- a/arch/mips/pci/pci-ar724x.c
|
|
+++ b/arch/mips/pci/pci-ar724x.c
|
|
@@ -9,6 +9,7 @@
|
|
*/
|
|
|
|
#include <linux/pci.h>
|
|
+#include <asm/mach-ath79/ath79.h>
|
|
#include <asm/mach-ath79/pci.h>
|
|
|
|
#define AR724X_PCI_CFG_BASE 0x14000000
|
|
@@ -16,9 +17,14 @@
|
|
#define AR724X_PCI_MEM_BASE 0x10000000
|
|
#define AR724X_PCI_MEM_SIZE 0x08000000
|
|
|
|
+#define AR7240_BAR0_WAR_VALUE 0xffff
|
|
+
|
|
static DEFINE_SPINLOCK(ar724x_pci_lock);
|
|
static void __iomem *ar724x_pci_devcfg_base;
|
|
|
|
+static u32 ar724x_pci_bar0_value;
|
|
+static bool ar724x_pci_bar0_is_cached;
|
|
+
|
|
static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where,
|
|
int size, uint32_t *value)
|
|
{
|
|
@@ -56,7 +62,14 @@ static int ar724x_pci_read(struct pci_bu
|
|
}
|
|
|
|
spin_unlock_irqrestore(&ar724x_pci_lock, flags);
|
|
- *value = data;
|
|
+
|
|
+ if (where == PCI_BASE_ADDRESS_0 && size == 4 &&
|
|
+ ar724x_pci_bar0_is_cached) {
|
|
+ /* use the cached value */
|
|
+ *value = ar724x_pci_bar0_value;
|
|
+ } else {
|
|
+ *value = data;
|
|
+ }
|
|
|
|
return PCIBIOS_SUCCESSFUL;
|
|
}
|
|
@@ -72,6 +85,27 @@ static int ar724x_pci_write(struct pci_b
|
|
if (devfn)
|
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
|
|
|
+ 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.
|
|
+ */
|
|
+ ar724x_pci_bar0_is_cached = true;
|
|
+ ar724x_pci_bar0_value = value;
|
|
+
|
|
+ value = AR7240_BAR0_WAR_VALUE;
|
|
+ } else {
|
|
+ ar724x_pci_bar0_is_cached = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
base = ar724x_pci_devcfg_base;
|
|
|
|
spin_lock_irqsave(&ar724x_pci_lock, flags);
|