diff options
Diffstat (limited to '')
-rw-r--r-- | arch/mips/bmips/Kconfig | 83 | ||||
-rw-r--r-- | arch/mips/bmips/Makefile | 2 | ||||
-rw-r--r-- | arch/mips/bmips/Platform | 6 | ||||
-rw-r--r-- | arch/mips/bmips/dma.c | 128 | ||||
-rw-r--r-- | arch/mips/bmips/irq.c | 42 | ||||
-rw-r--r-- | arch/mips/bmips/setup.c | 212 |
6 files changed, 473 insertions, 0 deletions
diff --git a/arch/mips/bmips/Kconfig b/arch/mips/bmips/Kconfig new file mode 100644 index 000000000..63dfc6950 --- /dev/null +++ b/arch/mips/bmips/Kconfig @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: GPL-2.0 +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 + +config DT_BCM97435SVMB + bool "BCM97435SVMB" + select BUILTIN_DTB + +config DT_COMTREND_VR3032U + bool "Comtrend VR-3032u" + select BUILTIN_DTB + +config DT_NETGEAR_CVG834G + bool "NETGEAR CVG834G" + select BUILTIN_DTB + +config DT_SFR_NEUFBOX4_SERCOMM + bool "SFR Neufbox 4 (Sercomm)" + select BUILTIN_DTB + +config DT_SFR_NEUFBOX6_SERCOMM + bool "SFR Neufbox 6 (Sercomm)" + select BUILTIN_DTB + +endchoice + +endif diff --git a/arch/mips/bmips/Makefile b/arch/mips/bmips/Makefile new file mode 100644 index 000000000..1165bf2ef --- /dev/null +++ b/arch/mips/bmips/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +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..1434ea31c --- /dev/null +++ b/arch/mips/bmips/Platform @@ -0,0 +1,6 @@ +# +# Broadcom Generic BMIPS kernel +# +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..daef44f68 --- /dev/null +++ b/arch/mips/bmips/dma.c @@ -0,0 +1,128 @@ +/* + * 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 <asm/bmips.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 + +dma_addr_t 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; +} + +phys_addr_t dma_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; +} + +bool bmips_rac_flush_disable; + +void arch_sync_dma_for_cpu_all(void) +{ + void __iomem *cbr = BMIPS_GET_CBR(); + u32 cfg; + + if (boot_cpu_type() != CPU_BMIPS3300 && + boot_cpu_type() != CPU_BMIPS4350 && + boot_cpu_type() != CPU_BMIPS4380) + return; + + if (unlikely(bmips_rac_flush_disable)) + return; + + /* Flush stale data out of the readahead cache */ + cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG); + __raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG); + __raw_readl(cbr + BMIPS_RAC_CONFIG); +} + +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 = kcalloc(len + 1, sizeof(struct bmips_dma_range), + 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..c4daa590b --- /dev/null +++ b/arch/mips/bmips/irq.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * 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> + +static const struct of_device_id smp_intc_dt_match[] = { + { .compatible = "brcm,bcm7038-l1-intc" }, + { .compatible = "brcm,bcm6345-l1-intc" }, + {} +}; + +unsigned int get_c0_compare_int(void) +{ + return CP0_LEGACY_COMPARE_IRQ; +} + +void __init arch_init_irq(void) +{ + struct device_node *dn; + + /* Only these controllers support SMP IRQ affinity */ + dn = of_find_matching_node(NULL, smp_intc_dt_match); + if (dn) + of_node_put(dn); + else + bmips_tp1_irqs = 0; + + irqchip_init(); +} + +IRQCHIP_DECLARE(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..16063081d --- /dev/null +++ b/arch/mips/bmips/setup.c @@ -0,0 +1,212 @@ +/* + * 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/memblock.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_clk.h> +#include <linux/of_fdt.h> +#include <linux/of_platform.h> +#include <linux/libfdt.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) + +extern bool bmips_rac_flush_disable; + +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 bcm6358_quirks(void) +{ + /* + * BCM3368/BCM6358 need special handling for their shared TLB, so + * disable SMP for now + */ + bmips_smp_enabled = 0; + + /* + * RAC flush causes kernel panics on BCM6358 when booting from TP1 + * because the bootloader is not initializing it properly. + */ + bmips_rac_flush_disable = !!(read_c0_brcm_cmt_local() & (1 << 31)); +} + +static void bcm6368_quirks(void) +{ + bcm63xx_fixup_cpu1(); +} + +static const struct bmips_quirk bmips_quirk_list[] = { + { "brcm,bcm3368", &bcm6358_quirks }, + { "brcm,bcm3384-viper", &bcm3384_viper_quirks }, + { "brcm,bcm33843-viper", &bcm3384_viper_quirks }, + { "brcm,bcm6328", &bcm6328_quirks }, + { "brcm,bcm6358", &bcm6358_quirks }, + { "brcm,bcm6362", &bcm6368_quirks }, + { "brcm,bcm6368", &bcm6368_quirks }, + { "brcm,bcm63168", &bcm6368_quirks }, + { "brcm,bcm63268", &bcm6368_quirks }, + { }, +}; + +void __init prom_init(void) +{ + bmips_cpu_setup(); + 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.rst */ + if (fw_arg0 == 0 && fw_arg1 == 0xffffffff) + dtb = phys_to_virt(fw_arg2); + else if (fw_passed_dtb) /* UHI interface or appended dtb */ + dtb = (void *)fw_passed_dtb; + else if (&__dtb_start != &__dtb_end) + dtb = (void *)__dtb_start; + else + panic("no dtb found"); + + __dt_setup_arch(dtb); + + 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); +} + +static int __init plat_dev_init(void) +{ + of_clk_init(NULL); + return 0; +} + +device_initcall(plat_dev_init); |