Commit eed0eabd authored by Paul Burton's avatar Paul Burton Committed by Ralf Baechle

MIPS: generic: Introduce generic DT-based board support

Introduce a "generic" platform, which aims to be board-agnostic by
making use of device trees passed by the boot protocol defined in the
MIPS UHI (Universal Hosting Interface) specification. Provision is made
for supporting boards which use a legacy boot protocol that can't be
changed, but adding support for such boards or any others is left to
followon patches.

Right now the built kernels expect to be loaded to 0x80100000, ie. in
kseg0. This is fine for the vast majority of MIPS platforms, but
nevertheless it would be good to remove this limitation in the future by
mapping the kernel via the TLB such that it can be loaded anywhere & map
itself appropriately.

Configuration is handled by dynamically generating configs using
scripts/kconfig/merge_config.sh, somewhat similar to the way powerpc
makes use of it. This allows for variations upon the configuration, eg.
differing architecture revisions or subsets of driver support for
differing boards, to be handled without having a large number of
defconfig files.
Signed-off-by: default avatarPaul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/14353/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent cf2a5e0b
......@@ -11,6 +11,7 @@ platforms += cavium-octeon
platforms += cobalt
platforms += dec
platforms += emma
platforms += generic
platforms += jazz
platforms += jz4740
platforms += lantiq
......
......@@ -73,6 +73,57 @@ choice
prompt "System type"
default SGI_IP22
config MIPS_GENERIC
bool "Generic board-agnostic MIPS kernel"
select BOOT_RAW
select BUILTIN_DTB
select CEVT_R4K
select CLKSRC_MIPS_GIC
select COMMON_CLK
select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_IRQ_EI
select CSRC_R4K
select DMA_PERDEV_COHERENT
select HW_HAS_PCI
select IRQ_MIPS_CPU
select LIBFDT
select MIPS_CPU_SCACHE
select MIPS_GIC
select MIPS_L1_CACHE_SHIFT_7
select NO_EXCEPT_FILL
select PCI_DRIVERS_GENERIC
select PINCTRL
select SMP_UP if SMP
select SYS_HAS_CPU_MIPS32_R1
select SYS_HAS_CPU_MIPS32_R2
select SYS_HAS_CPU_MIPS32_R6
select SYS_HAS_CPU_MIPS64_R1
select SYS_HAS_CPU_MIPS64_R2
select SYS_HAS_CPU_MIPS64_R6
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_LITTLE_ENDIAN
select SYS_SUPPORTS_MICROMIPS
select SYS_SUPPORTS_MIPS_CPS
select SYS_SUPPORTS_MIPS16
select SYS_SUPPORTS_MULTITHREADING
select SYS_SUPPORTS_RELOCATABLE
select SYS_SUPPORTS_SMARTMIPS
select USB_EHCI_BIG_ENDIAN_DESC if BIG_ENDIAN
select USB_EHCI_BIG_ENDIAN_MMIO if BIG_ENDIAN
select USB_OHCI_BIG_ENDIAN_DESC if BIG_ENDIAN
select USB_OHCI_BIG_ENDIAN_MMIO if BIG_ENDIAN
select USB_UHCI_BIG_ENDIAN_DESC if BIG_ENDIAN
select USB_UHCI_BIG_ENDIAN_MMIO if BIG_ENDIAN
select USE_OF
help
Select this to build a kernel which aims to support multiple boards,
generally using a flattened device tree passed from the bootloader
using the boot protocol defined in the UHI (Unified Hosting
Interface) specification.
config MIPS_ALCHEMY
bool "Alchemy processor based machines"
select ARCH_PHYS_ADDR_T_64BIT
......@@ -989,6 +1040,7 @@ source "arch/mips/ath79/Kconfig"
source "arch/mips/bcm47xx/Kconfig"
source "arch/mips/bcm63xx/Kconfig"
source "arch/mips/bmips/Kconfig"
source "arch/mips/generic/Kconfig"
source "arch/mips/jazz/Kconfig"
source "arch/mips/jz4740/Kconfig"
source "arch/mips/lantiq/Kconfig"
......
......@@ -264,6 +264,12 @@ KBUILD_CPPFLAGS += -DDATAOFFSET=$(if $(dataoffset-y),$(dataoffset-y),0)
bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) \
VMLINUX_ENTRY_ADDRESS=$(entry-y) \
PLATFORM=$(platform-y)
ifdef CONFIG_32BIT
bootvars-y += ADDR_BITS=32
endif
ifdef CONFIG_64BIT
bootvars-y += ADDR_BITS=64
endif
LDFLAGS += -m $(ld-emul)
......@@ -431,4 +437,54 @@ define archhelp
echo ' dtbs_install - Install dtbs to $(INSTALL_DTBS_PATH)'
echo
echo ' These will be default as appropriate for a configured platform.'
echo
echo ' If you are targeting a system supported by generic kernels you may'
echo ' configure the kernel for a given architecture target like so:'
echo
echo ' {micro32,32,64}{r1,r2,r6}{el,}_defconfig <BOARDS="list of boards">'
echo
echo ' Otherwise, the following default configurations are available:'
endef
generic_config_dir = $(srctree)/arch/$(ARCH)/configs/generic
generic_defconfigs :=
#
# If the user generates a generic kernel configuration without specifying a
# list of boards to include the config fragments for, default to including all
# available board config fragments.
#
ifeq ($(BOARDS),)
BOARDS = $(patsubst board-%.config,%,$(notdir $(wildcard $(generic_config_dir)/board-*.config)))
endif
#
# Generic kernel configurations which merge generic_defconfig with the
# appropriate config fragments from arch/mips/configs/generic/, resulting in
# the ability to easily configure the kernel for a given architecture,
# endianness & set of boards without duplicating the needed configuration in
# hundreds of defconfig files.
#
define gen_generic_defconfigs
$(foreach bits,$(1),$(foreach rev,$(2),$(foreach endian,$(3),
target := $(bits)$(rev)$(filter el,$(endian))_defconfig
generic_defconfigs += $$(target)
$$(target): $(generic_config_dir)/$(bits)$(rev).config
$$(target): $(generic_config_dir)/$(endian).config
)))
endef
$(eval $(call gen_generic_defconfigs,32 64,r1 r2 r6,eb el))
$(eval $(call gen_generic_defconfigs,micro32,r2,eb el))
.PHONY: $(generic_defconfigs)
$(generic_defconfigs):
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh \
-m -O $(objtree) $(srctree)/arch/$(ARCH)/configs/generic_defconfig $^ \
$(foreach board,$(BOARDS),$(generic_config_dir)/board-$(board).config)
$(Q)$(MAKE) olddefconfig
#
# Prevent generic merge_config rules attempting to merge single fragments
#
$(generic_config_dir)/%.config: ;
......@@ -111,13 +111,22 @@ targets += vmlinux.bz2.itb
targets += vmlinux.lzma.itb
targets += vmlinux.lzo.itb
ifeq ($(ADDR_BITS),32)
itb_addr_cells = 1
endif
ifeq ($(ADDR_BITS),64)
itb_addr_cells = 2
endif
quiet_cmd_cpp_its_S = ITS $@
cmd_cpp_its_S = $(CPP) $(cpp_flags) -P -C -o $@ $< \
-DKERNEL_NAME="\"Linux $(KERNELRELEASE)\"" \
-DVMLINUX_BINARY="\"$(3)\"" \
-DVMLINUX_COMPRESSION="\"$(2)\"" \
-DVMLINUX_LOAD_ADDRESS=$(VMLINUX_LOAD_ADDRESS) \
-DVMLINUX_ENTRY_ADDRESS=$(VMLINUX_ENTRY_ADDRESS)
-DVMLINUX_ENTRY_ADDRESS=$(VMLINUX_ENTRY_ADDRESS) \
-DADDR_BITS=$(ADDR_BITS) \
-DADDR_CELLS=$(itb_addr_cells)
$(obj)/vmlinux.its: $(srctree)/arch/mips/$(PLATFORM)/vmlinux.its.S FORCE
$(call if_changed_dep,cpp_its_S,none,vmlinux.bin)
......
CONFIG_CPU_MIPS32_R1=y
CONFIG_HIGHMEM=y
CONFIG_CPU_MIPS32_R2=y
CONFIG_MIPS_O32_FP64_SUPPORT=y
CONFIG_HIGHMEM=y
CONFIG_CPU_MIPS32_R6=y
CONFIG_HIGHMEM=y
CONFIG_CPU_MIPS64_R1=y
CONFIG_64BIT=y
CONFIG_MIPS32_O32=y
CONFIG_MIPS32_N32=y
CONFIG_CPU_MIPS64_R2=y
CONFIG_MIPS_O32_FP64_SUPPORT=y
CONFIG_64BIT=y
CONFIG_MIPS32_O32=y
CONFIG_MIPS32_N32=y
CONFIG_CPU_MIPS64_R6=y
CONFIG_64BIT=y
CONFIG_MIPS32_O32=y
CONFIG_MIPS32_N32=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CPU_MIPS32_R2=y
CONFIG_CPU_MICROMIPS=y
CONFIG_MIPS_O32_FP64_SUPPORT=y
CONFIG_HIGHMEM=y
CONFIG_MIPS_GENERIC=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_MIPS_CPS=y
CONFIG_CPU_HAS_MSA=y
CONFIG_HIGHMEM=y
CONFIG_NR_CPUS=2
CONFIG_MIPS_O32_FP64_SUPPORT=y
CONFIG_SYSVIPC=y
CONFIG_NO_HZ_IDLE=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y
CONFIG_BLK_CGROUP=y
CONFIG_CFS_BANDWIDTH=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_CGROUP_PIDS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_NAMESPACES=y
CONFIG_USER_NS=y
CONFIG_SCHED_AUTOGROUP=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_BPF_SYSCALL=y
CONFIG_USERFAULTFD=y
CONFIG_EMBEDDED=y
# CONFIG_SLUB_DEBUG is not set
# CONFIG_COMPAT_BRK is not set
CONFIG_CC_STACKPROTECTOR_REGULAR=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_TRIM_UNUSED_KSYMS=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_NETFILTER=y
# CONFIG_WIRELESS is not set
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
CONFIG_SCSI=y
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_HW_RANDOM=y
# CONFIG_HWMON is not set
CONFIG_MFD_SYSCON=y
CONFIG_HID_A4TECH=y
CONFIG_HID_APPLE=y
CONFIG_HID_BELKIN=y
CONFIG_HID_CHERRY=y
CONFIG_HID_CHICONY=y
CONFIG_HID_CYPRESS=y
CONFIG_HID_EZKEY=y
CONFIG_HID_KENSINGTON=y
CONFIG_HID_LOGITECH=y
CONFIG_HID_MICROSOFT=y
CONFIG_HID_MONTEREY=y
# CONFIG_USB_SUPPORT is not set
# CONFIG_MIPS_PLATFORM_DEVICES is not set
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_ENCRYPTION=y
CONFIG_FANOTIFY=y
CONFIG_FUSE_FS=y
CONFIG_CUSE=y
CONFIG_OVERLAY_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
# CONFIG_MISC_FILESYSTEMS is not set
CONFIG_NFS_FS=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_NFS_V4_1=y
CONFIG_NFS_V4_2=y
CONFIG_PRINTK_TIME=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_REDUCED=y
CONFIG_DEBUG_FS=y
# CONFIG_SCHED_DEBUG is not set
# CONFIG_FTRACE is not set
CONFIG_CMDLINE_BOOL=y
CONFIG_CMDLINE="earlycon"
# CONFIG_XZ_DEC_X86 is not set
# CONFIG_XZ_DEC_POWERPC is not set
# CONFIG_XZ_DEC_IA64 is not set
# CONFIG_XZ_DEC_ARM is not set
# CONFIG_XZ_DEC_ARMTHUMB is not set
# CONFIG_XZ_DEC_SPARC is not set
if MIPS_GENERIC
config LEGACY_BOARDS
bool
help
Select this from your board if the board must use a legacy, non-UHI,
boot protocol. This will cause the kernel to scan through the list of
supported machines calling their detect functions in turn if the
kernel is booted without being provided with an FDT via the UHI
boot protocol.
endif
#
# Copyright (C) 2016 Imagination Technologies
# Author: Paul Burton <paul.burton@imgtec.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
obj-y += init.o
obj-y += irq.o
obj-y += proc.o
#
# Copyright (C) 2016 Imagination Technologies
# Author: Paul Burton <paul.burton@imgtec.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
platform-$(CONFIG_MIPS_GENERIC) += generic/
cflags-$(CONFIG_MIPS_GENERIC) += -I$(srctree)/arch/mips/include/asm/mach-generic
load-$(CONFIG_MIPS_GENERIC) += 0xffffffff80100000
all-$(CONFIG_MIPS_GENERIC) := vmlinux.gz.itb
/*
* Copyright (C) 2016 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/irqchip.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <asm/fw/fw.h>
#include <asm/irq_cpu.h>
#include <asm/machine.h>
#include <asm/mips-cpc.h>
#include <asm/prom.h>
#include <asm/smp-ops.h>
#include <asm/time.h>
static __initdata const void *fdt;
static __initdata const struct mips_machine *mach;
static __initdata const void *mach_match_data;
void __init prom_init(void)
{
const struct mips_machine *check_mach;
const struct of_device_id *match;
if ((fw_arg0 == -2) && !fdt_check_header((void *)fw_arg1)) {
/*
* We booted using the UHI boot protocol, so we have been
* provided with the appropriate device tree for the board.
* Make use of it & search for any machine struct based upon
* the root compatible string.
*/
fdt = (void *)fw_arg1;
for_each_mips_machine(check_mach) {
match = mips_machine_is_compatible(check_mach, fdt);
if (match) {
mach = check_mach;
mach_match_data = match->data;
break;
}
}
} else if (IS_ENABLED(CONFIG_LEGACY_BOARDS)) {
/*
* We weren't booted using the UHI boot protocol, but do
* support some number of boards with legacy boot protocols.
* Attempt to find the right one.
*/
for_each_mips_machine(check_mach) {
if (!check_mach->detect)
continue;
if (!check_mach->detect())
continue;
mach = check_mach;
}
/*
* If we don't recognise the machine then we can't continue, so
* die here.
*/
BUG_ON(!mach);
/* Retrieve the machine's FDT */
fdt = mach->fdt;
}
BUG_ON(!fdt);
}
void __init *plat_get_fdt(void)
{
return (void *)fdt;
}
void __init plat_mem_setup(void)
{
if (mach && mach->fixup_fdt)
fdt = mach->fixup_fdt(fdt, mach_match_data);
strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
__dt_setup_arch((void *)fdt);
}
void __init device_tree_init(void)
{
int err;
unflatten_and_copy_device_tree();
mips_cpc_probe();
err = register_cps_smp_ops();
if (err)
err = register_up_smp_ops();
}
void __init plat_time_init(void)
{
struct device_node *np;
struct clk *clk;
of_clk_init(NULL);
if (!cpu_has_counter) {
mips_hpt_frequency = 0;
} else if (mach && mach->measure_hpt_freq) {
mips_hpt_frequency = mach->measure_hpt_freq();
} else {
np = of_get_cpu_node(0, NULL);
if (!np) {
pr_err("Failed to get CPU node\n");
return;
}
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
pr_err("Failed to get CPU clock: %ld\n", PTR_ERR(clk));
return;
}
mips_hpt_frequency = clk_get_rate(clk);
clk_put(clk);
switch (boot_cpu_type()) {
case CPU_20KC:
case CPU_25KF:
/* The counter runs at the CPU clock rate */
break;
default:
/* The counter runs at half the CPU clock rate */
mips_hpt_frequency /= 2;
break;
}
}
clocksource_probe();
}
void __init arch_init_irq(void)
{
struct device_node *intc_node;
intc_node = of_find_compatible_node(NULL, NULL,
"mti,cpu-interrupt-controller");
if (!cpu_has_veic && !intc_node)
mips_cpu_irq_init();
irqchip_init();
}
static int __init publish_devices(void)
{
if (!of_have_populated_dt())
panic("Device-tree not present");
if (of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL))
panic("Failed to populate DT");
return 0;
}
arch_initcall(publish_devices);
void __init prom_free_prom_memory(void)
{
}
/*
* Copyright (C) 2016 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/irqchip/mips-gic.h>
#include <linux/types.h>
#include <asm/irq.h>
int get_c0_fdc_int(void)
{
int mips_cpu_fdc_irq;
if (cpu_has_veic)
panic("Unimplemented!");
else if (gic_present)
mips_cpu_fdc_irq = gic_get_c0_fdc_int();
else if (cp0_fdc_irq >= 0)
mips_cpu_fdc_irq = MIPS_CPU_IRQ_BASE + cp0_fdc_irq;
else
mips_cpu_fdc_irq = -1;
return mips_cpu_fdc_irq;
}
int get_c0_perfcount_int(void)
{
int mips_cpu_perf_irq;
if (cpu_has_veic)
panic("Unimplemented!");
else if (gic_present)
mips_cpu_perf_irq = gic_get_c0_perfcount_int();
else if (cp0_perfcount_irq >= 0)
mips_cpu_perf_irq = MIPS_CPU_IRQ_BASE + cp0_perfcount_irq;
else
mips_cpu_perf_irq = -1;
return mips_cpu_perf_irq;
}
unsigned int get_c0_compare_int(void)
{
int mips_cpu_timer_irq;
if (cpu_has_veic)
panic("Unimplemented!");
else if (gic_present)
mips_cpu_timer_irq = gic_get_c0_compare_int();
else
mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
return mips_cpu_timer_irq;
}
/*
* Copyright (C) 2016 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/of.h>
#include <asm/bootinfo.h>
const char *get_system_type(void)
{
const char *str;
int err;
err = of_property_read_string(of_root, "model", &str);
if (!err)
return str;
err = of_property_read_string_index(of_root, "compatible", 0, &str);
if (!err)
return str;
return "Unknown";
}
/dts-v1/;
/ {
description = KERNEL_NAME;
#address-cells = <ADDR_CELLS>;
images {
kernel@0 {
description = KERNEL_NAME;
data = /incbin/(VMLINUX_BINARY);
type = "kernel";
arch = "mips";
os = "linux";
compression = VMLINUX_COMPRESSION;
load = /bits/ ADDR_BITS <VMLINUX_LOAD_ADDRESS>;
entry = /bits/ ADDR_BITS <VMLINUX_ENTRY_ADDRESS>;
hash@0 {
algo = "sha1";
};
};
};
configurations {
default = "conf@default";
conf@default {
description = "Generic Linux kernel";
kernel = "kernel@0";
};
};
};
/*
* Copyright (C) 2016 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef __MIPS_ASM_MACHINE_H__
#define __MIPS_ASM_MACHINE_H__
#include <linux/libfdt.h>
#include <linux/of.h>
struct mips_machine {
const struct of_device_id *matches;
const void *fdt;
bool (*detect)(void);
const void *(*fixup_fdt)(const void *fdt, const void *match_data);
unsigned int (*measure_hpt_freq)(void);
};
extern long __mips_machines_start;
extern long __mips_machines_end;
#define MIPS_MACHINE(name) \
static const struct mips_machine __mips_mach_##name \
__used __section(.mips.machines.init)
#define for_each_mips_machine(mach) \
for ((mach) = (struct mips_machine *)&__mips_machines_start; \
(mach) < (struct mips_machine *)&__mips_machines_end; \
(mach)++)
/**
* mips_machine_is_compatible() - check if a machine is compatible with an FDT
* @mach: the machine struct to check
* @fdt: the FDT to check for compatibility with
*
* Check whether the given machine @mach is compatible with the given flattened
* device tree @fdt, based upon the compatibility property of the root node.
*
* Return: the device id matched if any, else NULL
*/
static inline const struct of_device_id *
mips_machine_is_compatible(const struct mips_machine *mach, const void *fdt)
{
const struct of_device_id *match;
if (!mach->matches)
return NULL;
for (match = mach->matches; match->compatible; match++) {
if (fdt_node_check_compatible(fdt, 0, match->compatible) == 0)
return match;
}
return NULL;
}
#endif /* __MIPS_ASM_MACHINE_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment