diff options
Diffstat (limited to 'arch/mips/bmips')
-rw-r--r-- | arch/mips/bmips/Kconfig | 62 | ||||
-rw-r--r-- | arch/mips/bmips/Makefile | 1 | ||||
-rw-r--r-- | arch/mips/bmips/Platform | 7 | ||||
-rw-r--r-- | arch/mips/bmips/dma.c | 117 | ||||
-rw-r--r-- | arch/mips/bmips/irq.c | 38 | ||||
-rw-r--r-- | arch/mips/bmips/setup.c | 194 |
6 files changed, 419 insertions, 0 deletions
diff --git a/arch/mips/bmips/Kconfig b/arch/mips/bmips/Kconfig new file mode 100644 index 000000000..f35c84c01 --- /dev/null +++ b/arch/mips/bmips/Kconfig @@ -0,0 +1,62 @@ +if BMIPS_GENERIC + +choice + prompt "Built-in device tree" + help + Legacy bootloaders do not pass a DTB pointer to the kernel, so + if a "wrapper" is not being used, the kernel will need to include + a device tree that matches the target board. + + The builtin DTB will only be used if the firmware does not supply + a valid DTB. + +config DT_NONE + bool "None" + +config DT_BCM93384WVG + bool "BCM93384WVG Zephyr CPU" + select BUILTIN_DTB + +config DT_BCM93384WVG_VIPER + bool "BCM93384WVG Viper CPU (EXPERIMENTAL)" + select BUILTIN_DTB + +config DT_BCM96368MVWG + bool "BCM96368MVWG" + select BUILTIN_DTB + +config DT_BCM9EJTAGPRB + bool "BCM9EJTAGPRB" + select BUILTIN_DTB + +config DT_BCM97125CBMB + bool "BCM97125CBMB" + select BUILTIN_DTB + +config DT_BCM97346DBSMB + bool "BCM97346DBSMB" + select BUILTIN_DTB + +config DT_BCM97358SVMB + bool "BCM97358SVMB" + select BUILTIN_DTB + +config DT_BCM97360SVMB + bool "BCM97360SVMB" + select BUILTIN_DTB + +config DT_BCM97362SVMB + bool "BCM97362SVMB" + select BUILTIN_DTB + +config DT_BCM97420C + bool "BCM97420C" + select BUILTIN_DTB + +config DT_BCM97425SVMB + bool "BCM97425SVMB" + select BUILTIN_DTB + +endchoice + +endif diff --git a/arch/mips/bmips/Makefile b/arch/mips/bmips/Makefile new file mode 100644 index 000000000..a393955cb --- /dev/null +++ b/arch/mips/bmips/Makefile @@ -0,0 +1 @@ +obj-y += setup.o irq.o dma.o diff --git a/arch/mips/bmips/Platform b/arch/mips/bmips/Platform new file mode 100644 index 000000000..5f127fd7f --- /dev/null +++ b/arch/mips/bmips/Platform @@ -0,0 +1,7 @@ +# +# Broadcom Generic BMIPS kernel +# +platform-$(CONFIG_BMIPS_GENERIC) += bmips/ +cflags-$(CONFIG_BMIPS_GENERIC) += \ + -I$(srctree)/arch/mips/include/asm/mach-bmips/ +load-$(CONFIG_BMIPS_GENERIC) := 0xffffffff80010000 diff --git a/arch/mips/bmips/dma.c b/arch/mips/bmips/dma.c new file mode 100644 index 000000000..04790f4e1 --- /dev/null +++ b/arch/mips/bmips/dma.c @@ -0,0 +1,117 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com> + */ + +#define pr_fmt(fmt) "bmips-dma: " fmt + +#include <linux/device.h> +#include <linux/dma-direction.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <dma-coherence.h> + +/* + * BCM338x has configurable address translation windows which allow the + * peripherals' DMA addresses to be different from the Zephyr-visible + * physical addresses. e.g. usb_dma_addr = zephyr_pa ^ 0x08000000 + * + * If the "brcm,ubus" node has a "dma-ranges" property we will enable this + * translation globally using the provided information. This implements a + * very limited subset of "dma-ranges" support and it will probably be + * replaced by a more generic version later. + */ + +struct bmips_dma_range { + u32 child_addr; + u32 parent_addr; + u32 size; +}; + +static struct bmips_dma_range *bmips_dma_ranges; + +#define FLUSH_RAC 0x100 + +static dma_addr_t bmips_phys_to_dma(struct device *dev, phys_addr_t pa) +{ + struct bmips_dma_range *r; + + for (r = bmips_dma_ranges; r && r->size; r++) { + if (pa >= r->child_addr && + pa < (r->child_addr + r->size)) + return pa - r->child_addr + r->parent_addr; + } + return pa; +} + +dma_addr_t plat_map_dma_mem(struct device *dev, void *addr, size_t size) +{ + return bmips_phys_to_dma(dev, virt_to_phys(addr)); +} + +dma_addr_t plat_map_dma_mem_page(struct device *dev, struct page *page) +{ + return bmips_phys_to_dma(dev, page_to_phys(page)); +} + +unsigned long plat_dma_addr_to_phys(struct device *dev, dma_addr_t dma_addr) +{ + struct bmips_dma_range *r; + + for (r = bmips_dma_ranges; r && r->size; r++) { + if (dma_addr >= r->parent_addr && + dma_addr < (r->parent_addr + r->size)) + return dma_addr - r->parent_addr + r->child_addr; + } + return dma_addr; +} + +static int __init bmips_init_dma_ranges(void) +{ + struct device_node *np = + of_find_compatible_node(NULL, NULL, "brcm,ubus"); + const __be32 *data; + struct bmips_dma_range *r; + int len; + + if (!np) + return 0; + + data = of_get_property(np, "dma-ranges", &len); + if (!data) + goto out_good; + + len /= sizeof(*data) * 3; + if (!len) + goto out_bad; + + /* add a dummy (zero) entry at the end as a sentinel */ + bmips_dma_ranges = kzalloc(sizeof(struct bmips_dma_range) * (len + 1), + GFP_KERNEL); + if (!bmips_dma_ranges) + goto out_bad; + + for (r = bmips_dma_ranges; len; len--, r++) { + r->child_addr = be32_to_cpup(data++); + r->parent_addr = be32_to_cpup(data++); + r->size = be32_to_cpup(data++); + } + +out_good: + of_node_put(np); + return 0; + +out_bad: + pr_err("error parsing dma-ranges property\n"); + of_node_put(np); + return -EINVAL; +} +arch_initcall(bmips_init_dma_ranges); diff --git a/arch/mips/bmips/irq.c b/arch/mips/bmips/irq.c new file mode 100644 index 000000000..14552e58f --- /dev/null +++ b/arch/mips/bmips/irq.c @@ -0,0 +1,38 @@ +/* + * 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. + * + * Copyright (C) 2014 Broadcom Corporation + * Author: Kevin Cernekee <cernekee@gmail.com> + */ + +#include <linux/of.h> +#include <linux/irqchip.h> + +#include <asm/bmips.h> +#include <asm/irq.h> +#include <asm/irq_cpu.h> +#include <asm/time.h> + +unsigned int get_c0_compare_int(void) +{ + return CP0_LEGACY_COMPARE_IRQ; +} + +void __init arch_init_irq(void) +{ + struct device_node *dn; + + /* Only the STB (bcm7038) controller supports SMP IRQ affinity */ + dn = of_find_compatible_node(NULL, NULL, "brcm,bcm7038-l1-intc"); + if (dn) + of_node_put(dn); + else + bmips_tp1_irqs = 0; + + irqchip_init(); +} + +OF_DECLARE_2(irqchip, mips_cpu_intc, "mti,cpu-interrupt-controller", + mips_cpu_irq_of_init); diff --git a/arch/mips/bmips/setup.c b/arch/mips/bmips/setup.c new file mode 100644 index 000000000..fae800e8b --- /dev/null +++ b/arch/mips/bmips/setup.c @@ -0,0 +1,194 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * Copyright (C) 2014 Kevin Cernekee <cernekee@gmail.com> + */ + +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/bootmem.h> +#include <linux/clk-provider.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/smp.h> +#include <asm/addrspace.h> +#include <asm/bmips.h> +#include <asm/bootinfo.h> +#include <asm/cpu-type.h> +#include <asm/mipsregs.h> +#include <asm/prom.h> +#include <asm/smp-ops.h> +#include <asm/time.h> +#include <asm/traps.h> + +#define RELO_NORMAL_VEC BIT(18) + +#define REG_BCM6328_OTP ((void __iomem *)CKSEG1ADDR(0x1000062c)) +#define BCM6328_TP1_DISABLED BIT(9) + +static const unsigned long kbase = VMLINUX_LOAD_ADDRESS & 0xfff00000; + +struct bmips_quirk { + const char *compatible; + void (*quirk_fn)(void); +}; + +static void kbase_setup(void) +{ + __raw_writel(kbase | RELO_NORMAL_VEC, + BMIPS_GET_CBR() + BMIPS_RELO_VECTOR_CONTROL_1); + ebase = kbase; +} + +static void bcm3384_viper_quirks(void) +{ + /* + * Some experimental CM boxes are set up to let CM own the Viper TP0 + * and let Linux own TP1. This requires moving the kernel + * load address to a non-conflicting region (e.g. via + * CONFIG_PHYSICAL_START) and supplying an alternate DTB. + * If we detect this condition, we need to move the MIPS exception + * vectors up to an area that we own. + * + * This is distinct from the OTHER special case mentioned in + * smp-bmips.c (boot on TP1, but enable SMP, then TP0 becomes our + * logical CPU#1). For the Viper TP1 case, SMP is off limits. + * + * Also note that many BMIPS435x CPUs do not have a + * BMIPS_RELO_VECTOR_CONTROL_1 register, so it isn't safe to just + * write VMLINUX_LOAD_ADDRESS into that register on every SoC. + */ + board_ebase_setup = &kbase_setup; + bmips_smp_enabled = 0; +} + +static void bcm63xx_fixup_cpu1(void) +{ + /* + * The bootloader has set up the CPU1 reset vector at + * 0xa000_0200. + * This conflicts with the special interrupt vector (IV). + * The bootloader has also set up CPU1 to respond to the wrong + * IPI interrupt. + * Here we will start up CPU1 in the background and ask it to + * reconfigure itself then go back to sleep. + */ + memcpy((void *)0xa0000200, &bmips_smp_movevec, 0x20); + __sync(); + set_c0_cause(C_SW0); + cpumask_set_cpu(1, &bmips_booted_mask); +} + +static void bcm6328_quirks(void) +{ + /* Check CPU1 status in OTP (it is usually disabled) */ + if (__raw_readl(REG_BCM6328_OTP) & BCM6328_TP1_DISABLED) + bmips_smp_enabled = 0; + else + bcm63xx_fixup_cpu1(); +} + +static void bcm6368_quirks(void) +{ + bcm63xx_fixup_cpu1(); +} + +static const struct bmips_quirk bmips_quirk_list[] = { + { "brcm,bcm3384-viper", &bcm3384_viper_quirks }, + { "brcm,bcm33843-viper", &bcm3384_viper_quirks }, + { "brcm,bcm6328", &bcm6328_quirks }, + { "brcm,bcm6368", &bcm6368_quirks }, + { }, +}; + +void __init prom_init(void) +{ + register_bmips_smp_ops(); +} + +void __init prom_free_prom_memory(void) +{ +} + +const char *get_system_type(void) +{ + return "Generic BMIPS kernel"; +} + +void __init plat_time_init(void) +{ + struct device_node *np; + u32 freq; + + np = of_find_node_by_name(NULL, "cpus"); + if (!np) + panic("missing 'cpus' DT node"); + if (of_property_read_u32(np, "mips-hpt-frequency", &freq) < 0) + panic("missing 'mips-hpt-frequency' property"); + of_node_put(np); + + mips_hpt_frequency = freq; +} + +void __init plat_mem_setup(void) +{ + void *dtb; + const struct bmips_quirk *q; + + set_io_port_base(0); + ioport_resource.start = 0; + ioport_resource.end = ~0; + + /* intended to somewhat resemble ARM; see Documentation/arm/Booting */ + if (fw_arg0 == 0 && fw_arg1 == 0xffffffff) + dtb = phys_to_virt(fw_arg2); + else if (__dtb_start != __dtb_end) + dtb = (void *)__dtb_start; + else + panic("no dtb found"); + + __dt_setup_arch(dtb); + strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE); + + for (q = bmips_quirk_list; q->quirk_fn; q++) { + if (of_flat_dt_is_compatible(of_get_flat_dt_root(), + q->compatible)) { + q->quirk_fn(); + } + } +} + +void __init device_tree_init(void) +{ + struct device_node *np; + + unflatten_and_copy_device_tree(); + + /* Disable SMP boot unless both CPUs are listed in DT and !disabled */ + np = of_find_node_by_name(NULL, "cpus"); + if (np && of_get_available_child_count(np) <= 1) + bmips_smp_enabled = 0; + of_node_put(np); +} + +int __init plat_of_setup(void) +{ + return __dt_register_buses("simple-bus", NULL); +} + +arch_initcall(plat_of_setup); + +static int __init plat_dev_init(void) +{ + of_clk_init(NULL); + return 0; +} + +device_initcall(plat_dev_init); |