Skip to content

Commit

Permalink
dwc-otg: FIQ: Fix "bad mode in data abort handler"
Browse files Browse the repository at this point in the history
Create a semi-static mapping for the USB registers early in the boot
process, before additional kernel threads are started, so all threads
will have the mappings from the start. This avoids the need for
data aborts to lazily update them.

See: #2450

Signed-off-by: Floris Bos <bos@je-eigen-domein.nl>
  • Loading branch information
Phil Elwell committed Jul 19, 2018
1 parent 07f8f1d commit e0d2b2b
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 1 deletion.
69 changes: 69 additions & 0 deletions arch/arm/mach-bcm/board_bcm2835.c
Expand Up @@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/irqchip.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/clk/bcm2835.h>
#include <asm/system_info.h>

Expand All @@ -24,6 +25,9 @@
#include "platsmp.h"
#include <linux/dma-mapping.h>

#define BCM2835_USB_VIRT_BASE 0xf0980000
#define BCM2835_USB_VIRT_MPHI 0xf0006000

static void __init bcm2835_init(void)
{
struct device_node *np = of_find_node_by_path("/system");
Expand All @@ -44,6 +48,70 @@ static void __init bcm2835_init_early(void)
init_dma_coherent_pool_size(SZ_1M);
}

/*
* We need to map registers that are going to be accessed by the FIQ
* very early, before any kernel threads are spawned. Because if done
* later, the mapping tables are not updated instantly but lazily upon
* first access through a data abort handler. While that is fine
* when executing regular kernel code, if the first access in a specific
* thread happens while running FIQ code this will result in a panic.
*
* For more background see the following old mailing list thread:
* https://www.spinics.net/lists/arm-kernel/msg325250.html
*/
static int __init bcm2835_map_usb(unsigned long node, const char *uname,
int depth, void *data)
{
struct map_desc map[2];
const __be32 *reg;
int len;
unsigned long p2b_offset = *((unsigned long *) data);

if (!of_flat_dt_is_compatible(node, "brcm,bcm2708-usb"))
return 0;
reg = of_get_flat_dt_prop(node, "reg", &len);
if (!reg || len != (sizeof(unsigned long) * 4))
return 0;

/* Use information about the physical addresses of the
* registers from the device tree, but use legacy
* iotable_init() static mapping function to map them,
* as ioremap() is not functional at this stage in boot.
*/
map[0].virtual = (unsigned long) BCM2835_USB_VIRT_BASE;
map[0].pfn = __phys_to_pfn(be32_to_cpu(reg[0]) - p2b_offset);
map[0].length = be32_to_cpu(reg[1]);
map[0].type = MT_DEVICE;
map[1].virtual = (unsigned long) BCM2835_USB_VIRT_MPHI;
map[1].pfn = __phys_to_pfn(be32_to_cpu(reg[2]) - p2b_offset);
map[1].length = be32_to_cpu(reg[3]);
map[1].type = MT_DEVICE;
iotable_init(map, 2);

return 1;
}

static void __init bcm2835_map_io(void)
{
const __be32 *ranges;
int soc, len;
unsigned long p2b_offset;

debug_ll_io_init();

/* Find out how to map bus to physical address first from soc/ranges */
soc = of_get_flat_dt_subnode_by_name(of_get_flat_dt_root(), "soc");
if (soc < 0)
return;
ranges = of_get_flat_dt_prop(soc, "ranges", &len);
if (!ranges || len < (sizeof(unsigned long) * 3))
return;
p2b_offset = be32_to_cpu(ranges[0]) - be32_to_cpu(ranges[1]);

/* Now search for bcm2708-usb node in device tree */
of_scan_flat_dt(bcm2835_map_usb, &p2b_offset);
}

static const char * const bcm2835_compat[] = {
#ifdef CONFIG_ARCH_MULTI_V6
"brcm,bcm2835",
Expand All @@ -56,6 +124,7 @@ static const char * const bcm2835_compat[] = {
};

DT_MACHINE_START(BCM2835, "BCM2835")
.map_io = bcm2835_map_io,
.init_machine = bcm2835_init,
.init_early = bcm2835_init_early,
.dt_compat = bcm2835_compat,
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/host/dwc_otg/dwc_otg_driver.c
Expand Up @@ -837,7 +837,7 @@ static int dwc_otg_driver_probe(
retval = -ENOMEM;
goto fail;
}
dev_dbg(&_dev->dev, "base=0x%08x\n",
dev_info(&_dev->dev, "base=0x%08x\n",
(unsigned)dwc_otg_device->os_dep.base);
#endif

Expand Down

0 comments on commit e0d2b2b

Please sign in to comment.