* [RFC v2 5/8] filetype: add ELF type
From: Antony Pavlov @ 2016-12-05 9:40 UTC (permalink / raw)
To: barebox
In-Reply-To: <20161205094033.31569-1-antonynpavlov@gmail.com>
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
common/filetype.c | 5 +++++
include/filetype.h | 1 +
2 files changed, 6 insertions(+)
diff --git a/common/filetype.c b/common/filetype.c
index 8d72933..f204638 100644
--- a/common/filetype.c
+++ b/common/filetype.c
@@ -25,6 +25,7 @@
#include <errno.h>
#include <envfs.h>
#include <disks.h>
+#include <elf.h>
struct filetype_str {
const char *name; /* human readable filetype */
@@ -63,6 +64,7 @@ static const struct filetype_str filetype_str[] = {
[filetype_exe] = { "MS-DOS executable", "exe" },
[filetype_mxs_bootstream] = { "Freescale MXS bootstream", "mxsbs" },
[filetype_socfpga_xload] = { "SoCFPGA prebootloader image", "socfpga-xload" },
+ [filetype_elf] = { "ELF", "elf" },
};
const char *file_type_to_string(enum filetype f)
@@ -327,6 +329,9 @@ enum filetype file_detect_type(const void *_buf, size_t bufsize)
buf[7] == 0x47530000)
return filetype_ch_image_be;
+ if (strncmp(buf8, ELFMAG, 4) == 0)
+ return filetype_elf;
+
return filetype_unknown;
}
diff --git a/include/filetype.h b/include/filetype.h
index 65bd6ef..6fd2721 100644
--- a/include/filetype.h
+++ b/include/filetype.h
@@ -38,6 +38,7 @@ enum filetype {
filetype_xz_compressed,
filetype_mxs_bootstream,
filetype_socfpga_xload,
+ filetype_elf,
filetype_max,
};
--
2.10.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [RFC v2 3/8] resource: add create_resource() helper function
From: Antony Pavlov @ 2016-12-05 9:40 UTC (permalink / raw)
To: barebox
In-Reply-To: <20161205094033.31569-1-antonynpavlov@gmail.com>
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
common/resource.c | 15 +++++++++++++++
include/linux/ioport.h | 2 ++
2 files changed, 17 insertions(+)
diff --git a/common/resource.c b/common/resource.c
index e4bbe15..fa9ffd0 100644
--- a/common/resource.c
+++ b/common/resource.c
@@ -150,3 +150,18 @@ struct resource *request_ioport_region(const char *name,
return res;
}
+
+struct resource *create_resource(const char *name,
+ resource_size_t start, resource_size_t end)
+{
+ struct resource *t;
+
+ t = xzalloc(sizeof *t);
+ INIT_LIST_HEAD(&t->children);
+ t->parent = NULL;
+ t->name = xstrdup(name);
+ t->start = start;
+ t->end = end;
+
+ return t;
+}
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 3d375a8..2a944cc 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -140,6 +140,8 @@ struct resource *__request_region(struct resource *parent,
resource_size_t size);
int release_region(struct resource *res);
+struct resource *create_resource(const char *name,
+ resource_size_t start, resource_size_t end);
extern struct resource iomem_resource;
extern struct resource ioport_resource;
--
2.10.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [RFC v2 8/8] MIPS: malta: enable kexec
From: Antony Pavlov @ 2016-12-05 9:40 UTC (permalink / raw)
To: barebox
In-Reply-To: <20161205094033.31569-1-antonynpavlov@gmail.com>
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
arch/mips/Kconfig | 1 +
arch/mips/mach-malta/Makefile | 1 +
arch/mips/mach-malta/reboot.c | 104 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+)
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 06a516d..241bc27 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -48,6 +48,7 @@ config MACH_MIPS_MALTA
select HAS_DEBUG_LL
select GPIOLIB
select HW_HAS_PCI
+ select HAS_KEXEC
config MACH_MIPS_AR231X
bool "Atheros ar231x-based boards"
diff --git a/arch/mips/mach-malta/Makefile b/arch/mips/mach-malta/Makefile
index 0c5a701..20d9204 100644
--- a/arch/mips/mach-malta/Makefile
+++ b/arch/mips/mach-malta/Makefile
@@ -1,2 +1,3 @@
obj-y += reset.o
obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_KEXEC) += reboot.o
diff --git a/arch/mips/mach-malta/reboot.c b/arch/mips/mach-malta/reboot.c
new file mode 100644
index 0000000..5f68383
--- /dev/null
+++ b/arch/mips/mach-malta/reboot.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014, 2016 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * This file is part of barebox.
+ * See file CREDITS for list of people who contributed to this project.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <memory.h>
+#include <boot.h>
+#include <linux/reboot.h>
+#include "../../../lib/kexec/kexec.h"
+#include <asm/io.h>
+
+#define ENVP_ADDR 0x80002000l
+#define ENVP_NB_ENTRIES 16
+#define ENVP_ENTRY_SIZE 256
+
+static int reserve_yamon_prom_env(void)
+{
+ request_sdram_region("yamon env",
+ (unsigned long)ENVP_ADDR,
+ ENVP_NB_ENTRIES * ENVP_ENTRY_SIZE);
+
+ return 0;
+}
+late_initcall(reserve_yamon_prom_env);
+
+static void prom_set(uint32_t *prom_buf, int index,
+ const char *string, ...)
+{
+ va_list ap;
+ int32_t table_addr;
+
+ if (index >= ENVP_NB_ENTRIES)
+ return;
+
+ if (string == NULL) {
+ prom_buf[index] = 0;
+ return;
+ }
+
+ table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE;
+ prom_buf[index] = (ENVP_ADDR + table_addr);
+
+ va_start(ap, string);
+ vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap);
+ va_end(ap);
+}
+
+static inline void yamon_prom_set(void)
+{
+ void *prom_buf;
+ long prom_size;
+ int prom_index = 0;
+
+ /* Setup prom parameters. */
+ prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE);
+ prom_buf = (void *)ENVP_ADDR;
+
+ prom_set(prom_buf, prom_index++, "%s", getenv("global.bootm.image"));
+ prom_set(prom_buf, prom_index++, "%s", linux_bootargs_get());
+
+ prom_set(prom_buf, prom_index++, "memsize");
+ prom_set(prom_buf, prom_index++, "%i", 256 << 20);
+ prom_set(prom_buf, prom_index++, "modetty0");
+ prom_set(prom_buf, prom_index++, "38400n8r");
+ prom_set(prom_buf, prom_index++, NULL);
+}
+
+int reboot(int cmd)
+{
+ if (cmd == LINUX_REBOOT_CMD_KEXEC) {
+ extern unsigned long reboot_code_buffer;
+ extern unsigned long kexec_args[4];
+ void (*kexec_code_buffer)(void);
+
+ yamon_prom_set();
+
+ shutdown_barebox();
+
+ kexec_code_buffer = phys_to_virt(reboot_code_buffer);
+
+ kexec_args[0] = 2; /* number of arguments? */
+ kexec_args[1] = ENVP_ADDR;
+ kexec_args[2] = ENVP_ADDR + 8;
+ kexec_args[3] = 0x10000000; /* no matter */
+ kexec_code_buffer();
+ }
+
+ return -1;
+}
+EXPORT_SYMBOL(reboot);
--
2.10.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [RFC v2 7/8] MIPS: add kexec ELF loading support
From: Antony Pavlov @ 2016-12-05 9:40 UTC (permalink / raw)
To: barebox; +Cc: Peter Mamonov
In-Reply-To: <20161205094033.31569-1-antonynpavlov@gmail.com>
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
Signed-off-by: Peter Mamonov <pmamonov@gmail.com>
---
arch/mips/include/asm/elf.h | 8 +-
arch/mips/lib/Makefile | 3 +
arch/mips/lib/kexec-mips.c | 171 ++++++++++++++++++++++++++++++++++++++++
arch/mips/lib/relocate_kernel.S | 97 +++++++++++++++++++++++
4 files changed, 278 insertions(+), 1 deletion(-)
diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index b8b8219..bf974f5 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -17,12 +17,18 @@
#ifndef ELF_ARCH
+/* Legal values for e_machine (architecture). */
+
+#define EM_MIPS 8 /* MIPS R3000 big-endian */
+#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
+
#ifdef CONFIG_32BIT
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
-#define elf_check_arch(hdr) \
+#define elf_check_arch(x) ((x)->e_machine == EM_MIPS)
+
/*
* These are used to set parameters in the core dumps.
*/
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index d25d096..8dc4989 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -18,4 +18,7 @@ obj-$(CONFIG_CPU_MIPS64) += c-r4k.o
obj-$(CONFIG_CMD_MIPS_CPUINFO) += cpuinfo.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o
+obj-$(CONFIG_KEXEC) += kexec-mips.o
+obj-$(CONFIG_KEXEC) += relocate_kernel.o
+
pbl-y += ashldi3.o
diff --git a/arch/mips/lib/kexec-mips.c b/arch/mips/lib/kexec-mips.c
new file mode 100644
index 0000000..bf2a84b
--- /dev/null
+++ b/arch/mips/lib/kexec-mips.c
@@ -0,0 +1,171 @@
+/*
+ * kexec-mips.c - kexec for mips
+ * Copyright (C) 2007 Francesco Chiechi, Alessandro Rubini
+ * Copyright (C) 2007 Tvblob s.r.l.
+ *
+ * derived from ../ppc/kexec-mips.c
+ * Copyright (C) 2004, 2005 Albert Herranz
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/stddef.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include <memory.h>
+#include <elf.h>
+#include "../../../lib/kexec/kexec.h"
+
+static int elf_mips_probe(const char *buf, off_t len)
+{
+ struct mem_ehdr ehdr;
+ int result;
+
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ goto out;
+ }
+
+ /* Verify the architecuture specific bits */
+ if (ehdr.e_machine != EM_MIPS) {
+ /* for a different architecture */
+ printf("Not for this architecture.\n");
+ result = -1;
+ goto out;
+ }
+ result = 0;
+
+ out:
+ free_elf_info(&ehdr);
+
+ return result;
+}
+
+static int elf_mips_load(const char *buf, off_t len, struct kexec_info *info)
+{
+ struct mem_ehdr ehdr;
+ int result;
+ size_t i;
+
+ result = build_elf_exec_info(buf, len, &ehdr, 0);
+ if (result < 0) {
+ printf("ELF exec parse failed\n");
+ goto out;
+ }
+
+ /* Read in the PT_LOAD segments and remove CKSEG0 mask from address */
+ for (i = 0; i < ehdr.e_phnum; i++) {
+ struct mem_phdr *phdr;
+ phdr = &ehdr.e_phdr[i];
+ if (phdr->p_type == PT_LOAD) {
+ phdr->p_paddr = virt_to_phys((void *)phdr->p_paddr);
+ }
+ }
+
+ /* Load the ELF data */
+ result = elf_exec_load(&ehdr, info);
+ if (result < 0) {
+ printf("ELF exec load failed\n");
+ goto out;
+ }
+
+ info->entry = (void *)virt_to_phys((void *)ehdr.e_entry);
+
+out:
+ return result;
+}
+
+struct kexec_file_type kexec_file_type[] = {
+ {"elf-mips", elf_mips_probe, elf_mips_load },
+};
+int kexec_file_types = sizeof(kexec_file_type) / sizeof(kexec_file_type[0]);
+
+/*
+ * add_segment() should convert base to a physical address on mips,
+ * while the default is just to work with base as is */
+void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
+ unsigned long base, size_t memsz)
+{
+ add_segment_phys_virt(info, buf, bufsz,
+ virt_to_phys((void *)base), memsz, 1);
+}
+
+/* relocator parameters */
+extern unsigned long relocate_new_kernel;
+extern unsigned long relocate_new_kernel_size;
+extern unsigned long kexec_start_address;
+extern unsigned long kexec_segments;
+extern unsigned long kexec_nr_segments;
+
+unsigned long reboot_code_buffer;
+
+long kexec_load(void *entry, unsigned long nr_segments,
+ struct kexec_segment *segments, unsigned long flags)
+{
+ int i;
+ struct resource *elf;
+ resource_size_t start;
+ LIST_HEAD(elf_segments);
+
+ for (i = 0; i < nr_segments; i++) {
+ resource_size_t mem = (resource_size_t)segments[i].mem;
+
+ elf = create_resource("elf segment",
+ mem, mem + segments[i].memsz - 1);
+
+ list_add_used_region(&elf->sibling, &elf_segments);
+ }
+
+ if (check_room_for_elf(&elf_segments)) {
+ printf("ELF can't be loaded!\n");
+ return 0;
+ }
+
+ start = dcheck_res(&elf_segments);
+
+ /* relocate_new_kernel() copy by register (4 or 8 bytes)
+ so start address must be aligned to 4/8 */
+ start = (start + 15) & 0xfffffff0;
+
+ for (i = 0; i < nr_segments; i++) {
+ segments[i].mem = (void *)(phys_to_virt((unsigned long)segments[i].mem));
+ memcpy(phys_to_virt(start), segments[i].buf, segments[i].bufsz);
+ request_sdram_region("kexec relocatable segment",
+ (unsigned long)phys_to_virt(start),
+ (unsigned long)segments[i].bufsz);
+
+ /* relocate_new_kernel() copy by register (4 or 8 bytes)
+ so bufsz must be aligned to 4/8 */
+ segments[i].bufsz = (segments[i].bufsz + 15) & 0xfffffff0;
+ segments[i].buf = phys_to_virt(start);
+ start = start + segments[i].bufsz;
+ }
+
+ start = (start + 15) & 0xfffffff0;
+
+ reboot_code_buffer = start;
+
+ memcpy(phys_to_virt(start), &relocate_new_kernel,
+ relocate_new_kernel_size);
+ request_sdram_region("kexec relocator",
+ (unsigned long)phys_to_virt(start),
+ (unsigned long)relocate_new_kernel_size);
+
+ start = start + relocate_new_kernel_size;
+ start = (start + 15) & 0xfffffff0;
+
+ kexec_start_address = (unsigned long)phys_to_virt((unsigned long)entry);
+ kexec_segments = (unsigned long)phys_to_virt((unsigned long)start);
+ kexec_nr_segments = nr_segments;
+
+ memcpy(phys_to_virt(start), segments, nr_segments * sizeof(*segments));
+ request_sdram_region("kexec control segments",
+ (unsigned long)phys_to_virt(start),
+ (unsigned long)nr_segments * sizeof(*segments));
+
+ return 1;
+}
diff --git a/arch/mips/lib/relocate_kernel.S b/arch/mips/lib/relocate_kernel.S
new file mode 100644
index 0000000..1cd2ee5
--- /dev/null
+++ b/arch/mips/lib/relocate_kernel.S
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012, 2016 Antony Pavlov <antonynpavlov@gmail.com>
+ *
+ * based on relocate_kernel.S for kexec
+ * Created by <nschichan@corp.free.fr> on Thu Oct 12 17:49:57 2006
+ *
+ * This file is part of barebox.
+ * See file CREDITS for list of people who contributed to this project.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+
+LEAF(relocate_new_kernel)
+ .set push
+ .set reorder
+ PTR_L a0, arg0
+ PTR_L a1, arg1
+ PTR_L a2, arg2
+ PTR_L a3, arg3
+
+ PTR_L s0, kexec_segments
+ PTR_L s1, kexec_nr_segments
+ PTR_L s2, kexec_start_address
+
+process_segment:
+ PTR_L s4, (s0) /* buf */
+ PTR_L s5, SZREG (s0) /* bufsz */
+ PTR_L s6, 2*SZREG (s0) /* mem */
+
+copy_segment:
+ /* copy segment word by word */
+ REG_L s7, (s4)
+ REG_S s7, (s6)
+ PTR_ADD s4, s4, SZREG
+ PTR_ADD s6, s6, SZREG
+ LONG_SUB s5, s5, 1
+ bne s5, zero, copy_segment
+
+ LONG_SUB s1, s1, 1
+ beq s1, zero, done
+
+ PTR_ADD s0, s0, 4*SZREG
+
+ b process_segment
+
+done:
+ /* jump to kexec_start_address */
+ j s2
+ END(relocate_new_kernel)
+
+/* All parameters to new kernel are passed in registers a0-a3.
+ * kexec_args[0..3] are uses to prepare register values.
+ */
+
+kexec_args:
+ EXPORT(kexec_args)
+arg0: PTR 0x0
+arg1: PTR 0x0
+arg2: PTR 0x0
+arg3: PTR 0x0
+ .size kexec_args,PTRSIZE*4
+
+kexec_start_address:
+ EXPORT(kexec_start_address)
+ PTR 0x0
+ .size kexec_start_address, PTRSIZE
+
+kexec_segments:
+ EXPORT(kexec_segments)
+ PTR 0x0
+ .size kexec_segments, PTRSIZE
+
+kexec_nr_segments:
+ EXPORT(kexec_nr_segments)
+ PTR 0x0
+ .size kexec_nr_segments, PTRSIZE
+
+relocate_new_kernel_end:
+
+relocate_new_kernel_size:
+ EXPORT(relocate_new_kernel_size)
+ PTR relocate_new_kernel_end - relocate_new_kernel
+ .size relocate_new_kernel_size, PTRSIZE
+ .set pop
--
2.10.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [RFC v2 6/8] bootm: add kexec ELF support
From: Antony Pavlov @ 2016-12-05 9:40 UTC (permalink / raw)
To: barebox
In-Reply-To: <20161205094033.31569-1-antonynpavlov@gmail.com>
Also introduce reboot() for starting already loaded
via kexec ELF segments.
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
---
include/linux/reboot.h | 14 ++++++++++++++
lib/kexec/Makefile | 1 +
lib/kexec/kexec-bootm-elf.c | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+)
diff --git a/include/linux/reboot.h b/include/linux/reboot.h
new file mode 100644
index 0000000..454ed33
--- /dev/null
+++ b/include/linux/reboot.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_REBOOT_H
+#define _LINUX_REBOOT_H
+
+/*
+ * Commands accepted by the _reboot() system call.
+ *
+ * KEXEC Restart system using a previously loaded Linux kernel
+ */
+
+#define LINUX_REBOOT_CMD_KEXEC 0x45584543
+
+extern int reboot(int cmd);
+
+#endif /* _LINUX_REBOOT_H */
diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile
index 8febef1..2f3dc1d 100644
--- a/lib/kexec/Makefile
+++ b/lib/kexec/Makefile
@@ -1,3 +1,4 @@
obj-y += kexec.o
obj-y += kexec-elf.o
obj-y += kexec-elf-exec.o
+obj-y += kexec-bootm-elf.o
diff --git a/lib/kexec/kexec-bootm-elf.c b/lib/kexec/kexec-bootm-elf.c
new file mode 100644
index 0000000..ceef6c7
--- /dev/null
+++ b/lib/kexec/kexec-bootm-elf.c
@@ -0,0 +1,37 @@
+#include <bootm.h>
+#include <init.h>
+#include <binfmt.h>
+#include <errno.h>
+#include <linux/reboot.h>
+#include <environment.h>
+
+#include "kexec.h"
+
+static int do_bootm_elf(struct image_data *data)
+{
+ kexec_load_file(data->os_file, 0);
+ setenv("global.bootm.image", data->os_file);
+ reboot(LINUX_REBOOT_CMD_KEXEC);
+
+ return -ERESTARTSYS;
+}
+
+static struct image_handler elf_handler = {
+ .name = "ELF",
+ .bootm = do_bootm_elf,
+ .filetype = filetype_elf,
+};
+
+static struct binfmt_hook binfmt_elf_hook = {
+ .type = filetype_elf,
+ .exec = "bootm",
+};
+
+static int elf_register_image_handler(void)
+{
+ register_image_handler(&elf_handler);
+ binfmt_register(&binfmt_elf_hook);
+
+ return 0;
+}
+late_initcall(elf_register_image_handler);
--
2.10.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [RFC v2 4/8] import initial kexec stuff
From: Antony Pavlov @ 2016-12-05 9:40 UTC (permalink / raw)
To: barebox; +Cc: Peter Mamonov
In-Reply-To: <20161205094033.31569-1-antonynpavlov@gmail.com>
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
Signed-off-by: Peter Mamonov <pmamonov@gmail.com>
---
commands/Kconfig | 7 +
common/Kconfig | 3 +
lib/Makefile | 1 +
lib/kexec/Makefile | 3 +
lib/kexec/kexec-elf-exec.c | 82 +++++
lib/kexec/kexec-elf.c | 783 +++++++++++++++++++++++++++++++++++++++++++++
lib/kexec/kexec-elf.h | 86 +++++
lib/kexec/kexec.c | 151 +++++++++
lib/kexec/kexec.h | 89 ++++++
9 files changed, 1205 insertions(+)
diff --git a/commands/Kconfig b/commands/Kconfig
index 21d9212..80f6f2d 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -480,6 +480,13 @@ config CMD_SAVES
Save S-Record file to serial line with offset OFFS and length LEN.
+config KEXEC
+ bool
+ prompt "bootm ELF image support"
+ depends on CMD_BOOTM && HAS_KEXEC
+ help
+ Support using ELF Images.
+
config CMD_UIMAGE
select UIMAGE
tristate
diff --git a/common/Kconfig b/common/Kconfig
index ed472a0..ce0950a 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1160,3 +1160,6 @@ config HAS_DEBUG_LL
config DDR_SPD
bool
select CRC16
+
+config HAS_KEXEC
+ bool
diff --git a/lib/Makefile b/lib/Makefile
index 1be1742..dc256c1 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -59,3 +59,4 @@ obj-y += reed_solomon/
obj-$(CONFIG_RATP) += ratp.o
obj-y += list_sort.o
obj-y += int_sqrt.o
+obj-$(CONFIG_KEXEC) += kexec/
diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile
new file mode 100644
index 0000000..8febef1
--- /dev/null
+++ b/lib/kexec/Makefile
@@ -0,0 +1,3 @@
+obj-y += kexec.o
+obj-y += kexec-elf.o
+obj-y += kexec-elf-exec.o
diff --git a/lib/kexec/kexec-elf-exec.c b/lib/kexec/kexec-elf-exec.c
new file mode 100644
index 0000000..46c157c
--- /dev/null
+++ b/lib/kexec/kexec-elf-exec.c
@@ -0,0 +1,82 @@
+#include <common.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <elf.h>
+#include "kexec.h"
+#include "kexec-elf.h"
+
+int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags)
+{
+ struct mem_phdr *phdr, *end_phdr;
+ int result;
+
+ result = build_elf_info(buf, len, ehdr, flags);
+ if (result < 0) {
+ return result;
+ }
+
+ if (ehdr->e_type != ET_EXEC) {
+ printf("Not ELF type ET_EXEC\n");
+ return -1;
+ }
+
+ if (!ehdr->e_phdr) {
+ printf("No ELF program header\n");
+ return -1;
+ }
+
+ end_phdr = &ehdr->e_phdr[ehdr->e_phnum];
+ for (phdr = ehdr->e_phdr; phdr != end_phdr; phdr++) {
+ /* Kexec does not support loading interpreters.
+ * In addition this check keeps us from attempting
+ * to kexec ordinay executables.
+ */
+ if (phdr->p_type == PT_INTERP) {
+ printf("Requires an ELF interpreter\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info)
+{
+ int result;
+ size_t i;
+
+ if (!ehdr->e_phdr) {
+ printf("No program header?\n");
+ result = -1;
+ goto out;
+ }
+
+ /* Read in the PT_LOAD segments */
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ struct mem_phdr *phdr;
+ size_t size;
+
+ phdr = &ehdr->e_phdr[i];
+
+ if (phdr->p_type != PT_LOAD) {
+ continue;
+ }
+
+ size = phdr->p_filesz;
+
+ if (size > phdr->p_memsz) {
+ size = phdr->p_memsz;
+ }
+
+ add_segment(info,
+ phdr->p_data, size,
+ phdr->p_paddr, phdr->p_memsz);
+ }
+
+ result = 0;
+ out:
+ return result;
+}
diff --git a/lib/kexec/kexec-elf.c b/lib/kexec/kexec-elf.c
new file mode 100644
index 0000000..10a9aa4
--- /dev/null
+++ b/lib/kexec/kexec-elf.c
@@ -0,0 +1,783 @@
+#include <common.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <asm/io.h>
+#include "elf.h"
+#include "kexec.h"
+#include "kexec-elf.h"
+
+uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = le16_to_cpu(value);
+ } else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = be16_to_cpu(value);
+ }
+
+ return value;
+}
+
+uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = le32_to_cpu(value);
+ } else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = be32_to_cpu(value);
+ }
+
+ return value;
+}
+
+uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value)
+{
+ if (ehdr->ei_data == ELFDATA2LSB) {
+ value = le64_to_cpu(value);
+ } else if (ehdr->ei_data == ELFDATA2MSB) {
+ value = be64_to_cpu(value);
+ }
+
+ return value;
+}
+
+static int build_mem_elf32_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+ Elf32_Ehdr lehdr;
+
+ if ((size_t)len < sizeof(lehdr)) {
+ printf("Buffer is too small to hold ELF header\n");
+ return -1;
+ }
+
+ memcpy(&lehdr, buf, sizeof(lehdr));
+ if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf32_Ehdr)) {
+ printf("Bad ELF header size\n");
+ return -1;
+ }
+
+ if (elf32_to_cpu(ehdr, lehdr.e_entry) > UINT32_MAX) {
+ printf("ELF e_entry is too large\n");
+ return -1;
+ }
+
+ if (elf32_to_cpu(ehdr, lehdr.e_phoff) > UINT32_MAX) {
+ printf("ELF e_phoff is too large\n");
+ return -1;
+ }
+
+ if (elf32_to_cpu(ehdr, lehdr.e_shoff) > UINT32_MAX) {
+ printf("ELF e_shoff is too large\n");
+ return -1;
+ }
+
+ ehdr->e_type = elf16_to_cpu(ehdr, lehdr.e_type);
+ ehdr->e_machine = elf16_to_cpu(ehdr, lehdr.e_machine);
+ ehdr->e_version = elf32_to_cpu(ehdr, lehdr.e_version);
+ ehdr->e_entry = elf32_to_cpu(ehdr, lehdr.e_entry);
+ ehdr->e_phoff = elf32_to_cpu(ehdr, lehdr.e_phoff);
+ ehdr->e_shoff = elf32_to_cpu(ehdr, lehdr.e_shoff);
+ ehdr->e_flags = elf32_to_cpu(ehdr, lehdr.e_flags);
+ ehdr->e_phnum = elf16_to_cpu(ehdr, lehdr.e_phnum);
+ ehdr->e_shnum = elf16_to_cpu(ehdr, lehdr.e_shnum);
+ ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx);
+
+ if ((ehdr->e_phnum > 0) &&
+ (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf32_Phdr)))
+ {
+ printf("ELF bad program header size\n");
+ return -1;
+ }
+
+ if ((ehdr->e_shnum > 0) &&
+ (elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf32_Shdr)))
+ {
+ printf("ELF bad section header size\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int build_mem_elf64_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+ Elf64_Ehdr lehdr;
+
+ if ((size_t)len < sizeof(lehdr)) {
+ printf("Buffer is too small to hold ELF header\n");
+ return -1;
+ }
+
+ memcpy(&lehdr, buf, sizeof(lehdr));
+ if (elf16_to_cpu(ehdr, lehdr.e_ehsize) != sizeof(Elf64_Ehdr)) {
+ printf("Bad ELF header size\n");
+ return -1;
+ }
+
+ ehdr->e_type = elf16_to_cpu(ehdr, lehdr.e_type);
+ ehdr->e_machine = elf16_to_cpu(ehdr, lehdr.e_machine);
+ ehdr->e_version = elf32_to_cpu(ehdr, lehdr.e_version);
+ ehdr->e_entry = elf64_to_cpu(ehdr, lehdr.e_entry);
+ ehdr->e_phoff = elf64_to_cpu(ehdr, lehdr.e_phoff);
+ ehdr->e_shoff = elf64_to_cpu(ehdr, lehdr.e_shoff);
+ ehdr->e_flags = elf32_to_cpu(ehdr, lehdr.e_flags);
+ ehdr->e_phnum = elf16_to_cpu(ehdr, lehdr.e_phnum);
+ ehdr->e_shnum = elf16_to_cpu(ehdr, lehdr.e_shnum);
+ ehdr->e_shstrndx = elf16_to_cpu(ehdr, lehdr.e_shstrndx);
+
+ if ((ehdr->e_phnum > 0) &&
+ (elf16_to_cpu(ehdr, lehdr.e_phentsize) != sizeof(Elf64_Phdr)))
+ {
+ printf("ELF bad program header size\n");
+ return -1;
+ }
+
+ if ((ehdr->e_shnum > 0) &&
+ (elf16_to_cpu(ehdr, lehdr.e_shentsize) != sizeof(Elf64_Shdr)))
+ {
+ printf("ELF bad section header size\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int build_mem_ehdr(const char *buf, off_t len, struct mem_ehdr *ehdr)
+{
+ unsigned char e_ident[EI_NIDENT];
+ int result;
+
+ memset(ehdr, 0, sizeof(*ehdr));
+
+ if ((size_t)len < sizeof(e_ident)) {
+ printf("Buffer is too small to hold ELF e_ident\n");
+
+ return -1;
+ }
+
+ memcpy(e_ident, buf, sizeof(e_ident));
+
+ ehdr->ei_class = e_ident[EI_CLASS];
+ ehdr->ei_data = e_ident[EI_DATA];
+ if ( (ehdr->ei_class != ELFCLASS32) &&
+ (ehdr->ei_class != ELFCLASS64))
+ {
+ printf("Not a supported ELF class\n");
+ return -1;
+ }
+
+ if ( (ehdr->ei_data != ELFDATA2LSB) &&
+ (ehdr->ei_data != ELFDATA2MSB))
+ {
+ printf("Not a supported ELF data format\n");
+ return -1;
+ }
+
+ result = -1;
+ if (ehdr->ei_class == ELFCLASS32) {
+ result = build_mem_elf32_ehdr(buf, len, ehdr);
+ }
+
+ if (ehdr->ei_class == ELFCLASS64) {
+ result = build_mem_elf64_ehdr(buf, len, ehdr);
+ }
+
+ if (result < 0) {
+ return result;
+ }
+
+ if ((e_ident[EI_VERSION] != EV_CURRENT) ||
+ (ehdr->e_version != EV_CURRENT))
+ {
+ printf("Unknown ELF version\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int build_mem_elf32_phdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+ struct mem_phdr *phdr;
+ const char *pbuf;
+ Elf32_Phdr lphdr;
+
+ pbuf = buf + ehdr->e_phoff + (idx * sizeof(lphdr));
+ phdr = &ehdr->e_phdr[idx];
+ memcpy(&lphdr, pbuf, sizeof(lphdr));
+
+ if ( (elf32_to_cpu(ehdr, lphdr.p_filesz) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_memsz) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_offset) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_paddr) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_vaddr) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lphdr.p_align) > UINT32_MAX))
+ {
+ printf("Program segment size out of range\n");
+ return -1;
+ }
+
+ phdr->p_type = elf32_to_cpu(ehdr, lphdr.p_type);
+ phdr->p_paddr = elf32_to_cpu(ehdr, lphdr.p_paddr);
+ phdr->p_vaddr = elf32_to_cpu(ehdr, lphdr.p_vaddr);
+ phdr->p_filesz = elf32_to_cpu(ehdr, lphdr.p_filesz);
+ phdr->p_memsz = elf32_to_cpu(ehdr, lphdr.p_memsz);
+ phdr->p_offset = elf32_to_cpu(ehdr, lphdr.p_offset);
+ phdr->p_flags = elf32_to_cpu(ehdr, lphdr.p_flags);
+ phdr->p_align = elf32_to_cpu(ehdr, lphdr.p_align);
+
+ return 0;
+}
+
+static int build_mem_elf64_phdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+ struct mem_phdr *phdr;
+ const char *pbuf;
+ Elf64_Phdr lphdr;
+
+ pbuf = buf + ehdr->e_phoff + (idx * sizeof(lphdr));
+ phdr = &ehdr->e_phdr[idx];
+ memcpy(&lphdr, pbuf, sizeof(lphdr));
+ phdr->p_type = elf32_to_cpu(ehdr, lphdr.p_type);
+ phdr->p_paddr = elf64_to_cpu(ehdr, lphdr.p_paddr);
+ phdr->p_vaddr = elf64_to_cpu(ehdr, lphdr.p_vaddr);
+ phdr->p_filesz = elf64_to_cpu(ehdr, lphdr.p_filesz);
+ phdr->p_memsz = elf64_to_cpu(ehdr, lphdr.p_memsz);
+ phdr->p_offset = elf64_to_cpu(ehdr, lphdr.p_offset);
+ phdr->p_flags = elf32_to_cpu(ehdr, lphdr.p_flags);
+ phdr->p_align = elf64_to_cpu(ehdr, lphdr.p_align);
+
+ return 0;
+}
+
+static int build_mem_phdrs(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags)
+{
+ size_t phdr_size, mem_phdr_size, i;
+
+ /* e_phnum is at most 65535 so calculating
+ * the size of the program header cannot overflow.
+ */
+ /* Is the program header in the file buffer? */
+ phdr_size = 0;
+ if (ehdr->ei_class == ELFCLASS32) {
+ phdr_size = sizeof(Elf32_Phdr);
+ } else if (ehdr->ei_class == ELFCLASS64) {
+ phdr_size = sizeof(Elf64_Phdr);
+ } else {
+ printf("Invalid ei_class?\n");
+ return -1;
+ }
+ phdr_size *= ehdr->e_phnum;
+
+ /* Allocate the e_phdr array */
+ mem_phdr_size = sizeof(ehdr->e_phdr[0]) * ehdr->e_phnum;
+ ehdr->e_phdr = xmalloc(mem_phdr_size);
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ struct mem_phdr *phdr;
+ int result;
+
+ result = -1;
+ if (ehdr->ei_class == ELFCLASS32) {
+ result = build_mem_elf32_phdr(buf, ehdr, i);
+ }
+ if (ehdr->ei_class == ELFCLASS64) {
+ result = build_mem_elf64_phdr(buf, ehdr, i);
+ }
+
+ if (result < 0) {
+ return result;
+ }
+
+ /* Check the program headers to be certain
+ * they are safe to use.
+ */
+ phdr = &ehdr->e_phdr[i];
+ if ((phdr->p_paddr + phdr->p_memsz) < phdr->p_paddr) {
+ /* The memory address wraps */
+ printf("ELF address wrap around\n");
+ return -1;
+ }
+
+ /* Remember where the segment lives in the buffer */
+ phdr->p_data = buf + phdr->p_offset;
+ }
+
+ return 0;
+}
+
+static int build_mem_elf32_shdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+ struct mem_shdr *shdr;
+ const char *sbuf;
+ int size_ok;
+ Elf32_Shdr lshdr;
+
+ sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr));
+ shdr = &ehdr->e_shdr[idx];
+ memcpy(&lshdr, sbuf, sizeof(lshdr));
+
+ if ( (elf32_to_cpu(ehdr, lshdr.sh_flags) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_addr) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_offset) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_size) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_addralign) > UINT32_MAX) ||
+ (elf32_to_cpu(ehdr, lshdr.sh_entsize) > UINT32_MAX))
+ {
+ printf("Program section size out of range\n");
+ return -1;
+ }
+
+ shdr->sh_name = elf32_to_cpu(ehdr, lshdr.sh_name);
+ shdr->sh_type = elf32_to_cpu(ehdr, lshdr.sh_type);
+ shdr->sh_flags = elf32_to_cpu(ehdr, lshdr.sh_flags);
+ shdr->sh_addr = elf32_to_cpu(ehdr, lshdr.sh_addr);
+ shdr->sh_offset = elf32_to_cpu(ehdr, lshdr.sh_offset);
+ shdr->sh_size = elf32_to_cpu(ehdr, lshdr.sh_size);
+ shdr->sh_link = elf32_to_cpu(ehdr, lshdr.sh_link);
+ shdr->sh_info = elf32_to_cpu(ehdr, lshdr.sh_info);
+ shdr->sh_addralign = elf32_to_cpu(ehdr, lshdr.sh_addralign);
+ shdr->sh_entsize = elf32_to_cpu(ehdr, lshdr.sh_entsize);
+
+ /* Now verify sh_entsize */
+ size_ok = 0;
+ switch(shdr->sh_type) {
+ case SHT_SYMTAB:
+ size_ok = shdr->sh_entsize == sizeof(Elf32_Sym);
+ break;
+ case SHT_RELA:
+ size_ok = shdr->sh_entsize == sizeof(Elf32_Rela);
+ break;
+ case SHT_DYNAMIC:
+ size_ok = shdr->sh_entsize == sizeof(Elf32_Dyn);
+ break;
+ case SHT_REL:
+ size_ok = shdr->sh_entsize == sizeof(Elf32_Rel);
+ break;
+ case SHT_NOTE:
+ case SHT_NULL:
+ case SHT_PROGBITS:
+ case SHT_HASH:
+ case SHT_NOBITS:
+ default:
+ /* This is a section whose entsize requirements
+ * I don't care about. If I don't know about
+ * the section I can't care about it's entsize
+ * requirements.
+ */
+ size_ok = 1;
+ break;
+ }
+
+ if (!size_ok) {
+ printf("Bad section header(%x) entsize: %lld\n",
+ shdr->sh_type, shdr->sh_entsize);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehdr, int idx)
+{
+ struct mem_shdr *shdr;
+ const char *sbuf;
+ int size_ok;
+ Elf64_Shdr lshdr;
+
+ sbuf = buf + ehdr->e_shoff + (idx * sizeof(lshdr));
+ shdr = &ehdr->e_shdr[idx];
+ memcpy(&lshdr, sbuf, sizeof(lshdr));
+ shdr->sh_name = elf32_to_cpu(ehdr, lshdr.sh_name);
+ shdr->sh_type = elf32_to_cpu(ehdr, lshdr.sh_type);
+ shdr->sh_flags = elf64_to_cpu(ehdr, lshdr.sh_flags);
+ shdr->sh_addr = elf64_to_cpu(ehdr, lshdr.sh_addr);
+ shdr->sh_offset = elf64_to_cpu(ehdr, lshdr.sh_offset);
+ shdr->sh_size = elf64_to_cpu(ehdr, lshdr.sh_size);
+ shdr->sh_link = elf32_to_cpu(ehdr, lshdr.sh_link);
+ shdr->sh_info = elf32_to_cpu(ehdr, lshdr.sh_info);
+ shdr->sh_addralign = elf64_to_cpu(ehdr, lshdr.sh_addralign);
+ shdr->sh_entsize = elf64_to_cpu(ehdr, lshdr.sh_entsize);
+
+ /* Now verify sh_entsize */
+ size_ok = 0;
+ switch(shdr->sh_type) {
+ case SHT_SYMTAB:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Sym);
+ break;
+ case SHT_RELA:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Rela);
+ break;
+ case SHT_DYNAMIC:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Dyn);
+ break;
+ case SHT_REL:
+ size_ok = shdr->sh_entsize == sizeof(Elf64_Rel);
+ break;
+ case SHT_NOTE:
+ case SHT_NULL:
+ case SHT_PROGBITS:
+ case SHT_HASH:
+ case SHT_NOBITS:
+ default:
+ /* This is a section whose entsize requirements
+ * I don't care about. If I don't know about
+ * the section I can't care about it's entsize
+ * requirements.
+ */
+ size_ok = 1;
+ break;
+ }
+
+ if (!size_ok) {
+ printf("Bad section header(%x) entsize: %lld\n",
+ shdr->sh_type, shdr->sh_entsize);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int build_mem_shdrs(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags)
+{
+ size_t shdr_size, mem_shdr_size, i;
+
+ /* e_shnum is at most 65536 so calculating
+ * the size of the section header cannot overflow.
+ */
+ /* Is the program header in the file buffer? */
+ shdr_size = 0;
+ if (ehdr->ei_class == ELFCLASS32) {
+ shdr_size = sizeof(Elf32_Shdr);
+ } else if (ehdr->ei_class == ELFCLASS64) {
+ shdr_size = sizeof(Elf64_Shdr);
+ } else {
+ printf("Invalid ei_class?\n");
+ return -1;
+ }
+ shdr_size *= ehdr->e_shnum;
+
+ /* Allocate the e_shdr array */
+ mem_shdr_size = sizeof(ehdr->e_shdr[0]) * ehdr->e_shnum;
+ ehdr->e_shdr = xmalloc(mem_shdr_size);
+
+ for (i = 0; i < ehdr->e_shnum; i++) {
+ struct mem_shdr *shdr;
+ int result;
+
+ result = -1;
+ if (ehdr->ei_class == ELFCLASS32) {
+ result = build_mem_elf32_shdr(buf, ehdr, i);
+ }
+ if (ehdr->ei_class == ELFCLASS64) {
+ result = build_mem_elf64_shdr(buf, ehdr, i);
+ }
+
+ if (result < 0) {
+ return result;
+ }
+
+ /* Check the section headers to be certain
+ * they are safe to use.
+ */
+ shdr = &ehdr->e_shdr[i];
+ if ((shdr->sh_addr + shdr->sh_size) < shdr->sh_addr) {
+ printf("ELF address wrap around\n");
+ return -1;
+ }
+
+ /* Remember where the section lives in the buffer */
+ shdr->sh_data = (unsigned char *)(buf + shdr->sh_offset);
+ }
+
+ return 0;
+}
+
+static void read_nhdr(const struct mem_ehdr *ehdr,
+ ElfNN_Nhdr *hdr, const unsigned char *note)
+{
+ memcpy(hdr, note, sizeof(*hdr));
+ hdr->n_namesz = elf32_to_cpu(ehdr, hdr->n_namesz);
+ hdr->n_descsz = elf32_to_cpu(ehdr, hdr->n_descsz);
+ hdr->n_type = elf32_to_cpu(ehdr, hdr->n_type);
+}
+
+static int build_mem_notes(struct mem_ehdr *ehdr)
+{
+ const unsigned char *note_start, *note_end, *note;
+ size_t note_size, i;
+
+ /* First find the note segment or section */
+ note_start = note_end = NULL;
+
+ for (i = 0; !note_start && (i < ehdr->e_phnum); i++) {
+ struct mem_phdr *phdr = &ehdr->e_phdr[i];
+ /*
+ * binutils <= 2.17 has a bug where it can create the
+ * PT_NOTE segment with an offset of 0. Therefore
+ * check p_offset > 0.
+ *
+ * See: http://sourceware.org/bugzilla/show_bug.cgi?id=594
+ */
+ if (phdr->p_type == PT_NOTE && phdr->p_offset) {
+ note_start = (unsigned char *)phdr->p_data;
+ note_end = note_start + phdr->p_filesz;
+ }
+ }
+
+ for (i = 0; !note_start && (i < ehdr->e_shnum); i++) {
+ struct mem_shdr *shdr = &ehdr->e_shdr[i];
+ if (shdr->sh_type == SHT_NOTE) {
+ note_start = shdr->sh_data;
+ note_end = note_start + shdr->sh_size;
+ }
+ }
+
+ if (!note_start) {
+ return 0;
+ }
+
+ /* Walk through and count the notes */
+ ehdr->e_notenum = 0;
+ for (note = note_start; note < note_end; note += note_size) {
+ ElfNN_Nhdr hdr;
+ read_nhdr(ehdr, &hdr, note);
+ note_size = sizeof(hdr);
+ note_size += (hdr.n_namesz + 3) & ~3;
+ note_size += (hdr.n_descsz + 3) & ~3;
+ ehdr->e_notenum += 1;
+ }
+
+ /* Now walk and normalize the notes */
+ ehdr->e_note = xmalloc(sizeof(*ehdr->e_note) * ehdr->e_notenum);
+ for (i = 0, note = note_start; note < note_end;
+ note += note_size, i++) {
+ const unsigned char *name, *desc;
+ ElfNN_Nhdr hdr;
+ read_nhdr(ehdr, &hdr, note);
+ note_size = sizeof(hdr);
+ name = note + note_size;
+ note_size += (hdr.n_namesz + 3) & ~3;
+ desc = note + note_size;
+ note_size += (hdr.n_descsz + 3) & ~3;
+
+ if ((hdr.n_namesz != 0) && (name[hdr.n_namesz -1] != '\0')) {
+ /* If note name string is not null terminated, just
+ * warn user about it and continue processing. This
+ * allows us to parse /proc/kcore on older kernels
+ * where /proc/kcore elf notes were not null
+ * terminated. It has been fixed in 2.6.19.
+ */
+ printf("Warning: Elf Note name is not null "
+ "terminated\n");
+ }
+ ehdr->e_note[i].n_type = hdr.n_type;
+ ehdr->e_note[i].n_name = (char *)name;
+ ehdr->e_note[i].n_desc = desc;
+ ehdr->e_note[i].n_descsz = hdr.n_descsz;
+
+ }
+
+ return 0;
+}
+
+void free_elf_info(struct mem_ehdr *ehdr)
+{
+ free(ehdr->e_phdr);
+ free(ehdr->e_shdr);
+ memset(ehdr, 0, sizeof(*ehdr));
+}
+
+int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags)
+{
+ int result;
+
+ result = build_mem_ehdr(buf, len, ehdr);
+ if (result < 0) {
+ return result;
+ }
+
+ if ((ehdr->e_phoff > 0) && (ehdr->e_phnum > 0)) {
+ result = build_mem_phdrs(buf, len, ehdr, flags);
+ if (result < 0) {
+ free_elf_info(ehdr);
+ return result;
+ }
+ }
+
+ if ((ehdr->e_shoff > 0) && (ehdr->e_shnum > 0)) {
+ result = build_mem_shdrs(buf, len, ehdr, flags);
+ if (result < 0) {
+ free_elf_info(ehdr);
+ return result;
+ }
+ }
+
+ result = build_mem_notes(ehdr);
+ if (result < 0) {
+ free_elf_info(ehdr);
+ return result;
+ }
+
+ return 0;
+}
+
+int check_room_for_elf(struct list_head *elf_segments)
+{
+ struct memory_bank *bank;
+ struct resource *res, *r;
+
+ list_for_each_entry(r, elf_segments, sibling) {
+ int got_bank;
+
+ got_bank = 0;
+ for_each_memory_bank(bank) {
+ resource_size_t start, end;
+
+ res = bank->res;
+
+ start = virt_to_phys((void *)res->start);
+ end = virt_to_phys((void *)res->end);
+
+ if ((start <= r->start) && (end >= r->end)) {
+ got_bank = 1;
+ break;
+ }
+ }
+
+ if (!got_bank)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* sort by size */
+static int compare(struct list_head *a, struct list_head *b)
+{
+ struct resource *ra = (struct resource *)list_entry(a, struct resource, sibling);
+ struct resource *rb = (struct resource *)list_entry(b, struct resource, sibling);
+ resource_size_t sa, sb;
+
+ sa = ra->end - ra->start;
+ sb = rb->end - rb->start;
+
+ if (sa > sb)
+ return -1;
+ if (sa < sb)
+ return 1;
+ return 0;
+}
+
+void list_add_used_region(struct list_head *new, struct list_head *head)
+{
+ struct list_head *pos, *insert = head;
+ struct resource *rb =
+ (struct resource *)list_entry(new, struct resource, sibling);
+ struct list_head *n;
+
+ /* rb --- new region */
+ list_for_each_safe(pos, n, head) {
+ struct resource *ra = (struct resource *)list_entry(pos, struct resource, sibling);
+
+ if (((rb->end >= ra->start) && (rb->end <= ra->end))
+ || ((rb->start >= ra->start) && (rb->start <= ra->end))
+ || ((rb->start >= ra->start) && (rb->end <= ra->end))
+ || ((ra->start >= rb->start) && (ra->end <= rb->end))
+ || (ra->start == rb->end + 1)
+ || (rb->start == ra->end + 1)) {
+ rb->start = min(ra->start, rb->start);
+ rb->end = max(ra->end, rb->end);
+ rb->name = "join";
+ list_del(pos);
+ }
+ }
+
+ list_for_each(pos, head) {
+ struct resource *ra = (struct resource *)list_entry(pos, struct resource, sibling);
+
+ if (ra->start < rb->start)
+ continue;
+
+ insert = pos;
+ break;
+ }
+
+ list_add_tail(new, insert);
+}
+
+resource_size_t dcheck_res(struct list_head *elf_segments)
+{
+ struct memory_bank *bank;
+ struct resource *res, *r, *t;
+
+ LIST_HEAD(elf_relocate_banks);
+ LIST_HEAD(elf_relocate_banks_size_sorted);
+ LIST_HEAD(used_regions);
+
+ for_each_memory_bank(bank) {
+ res = bank->res;
+
+ list_for_each_entry(r, &res->children, sibling) {
+ t = create_resource("tmp",
+ virt_to_phys((void *)r->start),
+ virt_to_phys((void *)r->end));
+ list_add_used_region(&t->sibling, &used_regions);
+ }
+ }
+
+ list_for_each_entry(r, elf_segments, sibling) {
+ t = create_resource(r->name, r->start, r->end);
+ list_add_used_region(&t->sibling, &used_regions);
+ }
+
+ for_each_memory_bank(bank) {
+ resource_size_t start;
+
+ res = bank->res;
+ res = create_resource("tmp",
+ virt_to_phys((void *)res->start),
+ virt_to_phys((void *)res->end));
+ start = res->start;
+
+ list_for_each_entry(r, &used_regions, sibling) {
+ if (res->start > r->end)
+ continue;
+
+ if (res->end < r->start)
+ continue;
+
+ if (r->start - start) {
+ struct resource *t;
+
+ t = create_resource("ELF buffer", start, r->start - 1);
+ list_add_used_region(&t->sibling, &elf_relocate_banks);
+ }
+ start = r->end + 1;
+ }
+
+ if (res->end - start) {
+ struct resource *t;
+
+ t = create_resource("ELF buffer", start, res->end);
+ list_add_used_region(&t->sibling, &elf_relocate_banks);
+ }
+ }
+
+ list_for_each_entry(r, &elf_relocate_banks, sibling) {
+ struct resource *t;
+
+ t = create_resource("ELF buffer", r->start, r->end);
+ list_add_sort(&t->sibling,
+ &elf_relocate_banks_size_sorted, compare);
+ }
+
+ r = list_first_entry(&elf_relocate_banks_size_sorted, struct resource, sibling);
+
+ /* FIXME */
+ return r->start;
+}
diff --git a/lib/kexec/kexec-elf.h b/lib/kexec/kexec-elf.h
new file mode 100644
index 0000000..cf4be78
--- /dev/null
+++ b/lib/kexec/kexec-elf.h
@@ -0,0 +1,86 @@
+#ifndef KEXEC_ELF_H
+#define KEXEC_ELF_H
+
+struct kexec_info;
+
+struct mem_ehdr {
+ unsigned ei_class;
+ unsigned ei_data;
+ unsigned e_type;
+ unsigned e_machine;
+ unsigned e_version;
+ unsigned e_flags;
+ unsigned e_phnum;
+ unsigned e_shnum;
+ unsigned e_shstrndx;
+ unsigned long long e_entry;
+ unsigned long long e_phoff;
+ unsigned long long e_shoff;
+ unsigned e_notenum;
+ struct mem_phdr *e_phdr;
+ struct mem_shdr *e_shdr;
+ struct mem_note *e_note;
+ unsigned long rel_addr, rel_size;
+};
+
+struct mem_phdr {
+ unsigned long long p_paddr;
+ unsigned long long p_vaddr;
+ unsigned long long p_filesz;
+ unsigned long long p_memsz;
+ unsigned long long p_offset;
+ const char *p_data;
+ unsigned p_type;
+ unsigned p_flags;
+ unsigned long long p_align;
+};
+
+struct mem_shdr {
+ unsigned sh_name;
+ unsigned sh_type;
+ unsigned long long sh_flags;
+ unsigned long long sh_addr;
+ unsigned long long sh_offset;
+ unsigned long long sh_size;
+ unsigned sh_link;
+ unsigned sh_info;
+ unsigned long long sh_addralign;
+ unsigned long long sh_entsize;
+ const unsigned char *sh_data;
+};
+
+struct mem_note {
+ unsigned n_type;
+ unsigned n_descsz;
+ const char *n_name;
+ const void *n_desc;
+};
+
+/* The definition of an ELF note does not vary depending
+ * on ELFCLASS.
+ */
+typedef struct
+{
+ uint32_t n_namesz; /* Length of the note's name. */
+ uint32_t n_descsz; /* Length of the note's descriptor. */
+ uint32_t n_type; /* Type of the note. */
+} ElfNN_Nhdr;
+
+extern void free_elf_info(struct mem_ehdr *ehdr);
+extern int build_elf_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
+ uint32_t flags);
+extern int build_elf_exec_info(const char *buf, off_t len,
+ struct mem_ehdr *ehdr, uint32_t flags);
+
+extern int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info);
+
+uint16_t elf16_to_cpu(const struct mem_ehdr *ehdr, uint16_t value);
+uint32_t elf32_to_cpu(const struct mem_ehdr *ehdr, uint32_t value);
+uint64_t elf64_to_cpu(const struct mem_ehdr *ehdr, uint64_t value);
+
+unsigned long elf_max_addr(const struct mem_ehdr *ehdr);
+int check_room_for_elf(struct list_head *elf_segments);
+resource_size_t dcheck_res(struct list_head *elf_segments);
+void list_add_used_region(struct list_head *new, struct list_head *head);
+
+#endif /* KEXEC_ELF_H */
diff --git a/lib/kexec/kexec.c b/lib/kexec/kexec.c
new file mode 100644
index 0000000..d5bebfb
--- /dev/null
+++ b/lib/kexec/kexec.c
@@ -0,0 +1,151 @@
+/*
+ * kexec: Linux boots Linux
+ *
+ * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com)
+ *
+ * Modified (2007-05-15) by Francesco Chiechi to rudely handle mips platform
+ *
+ * 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 (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <common.h>
+#include <fs.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <asm/io.h>
+
+#include <libfile.h>
+
+#include "kexec.h"
+#include "kexec-elf.h"
+
+static int sort_segments(struct kexec_info *info)
+{
+ int i, j;
+ void *end;
+
+ /* Do a stupid insertion sort... */
+ for (i = 0; i < info->nr_segments; i++) {
+ int tidx;
+ struct kexec_segment temp;
+ tidx = i;
+ for (j = i +1; j < info->nr_segments; j++) {
+ if (info->segment[j].mem < info->segment[tidx].mem) {
+ tidx = j;
+ }
+ }
+ if (tidx != i) {
+ temp = info->segment[tidx];
+ info->segment[tidx] = info->segment[i];
+ info->segment[i] = temp;
+ }
+ }
+
+ /* Now see if any of the segments overlap */
+ end = 0;
+ for (i = 0; i < info->nr_segments; i++) {
+ if (end > info->segment[i].mem) {
+ printf("Overlapping memory segments at %p\n",
+ end);
+ return -1;
+ }
+ end = ((char *)info->segment[i].mem) + info->segment[i].memsz;
+ }
+
+ return 0;
+}
+
+void add_segment_phys_virt(struct kexec_info *info,
+ const void *buf, size_t bufsz,
+ unsigned long base, size_t memsz, int phys)
+{
+ size_t size;
+ int pagesize;
+
+ if (bufsz > memsz) {
+ bufsz = memsz;
+ }
+
+ /* Forget empty segments */
+ if (memsz == 0) {
+ return;
+ }
+
+ /* Round memsz up to a multiple of pagesize */
+ pagesize = 4096;
+ memsz = (memsz + (pagesize - 1)) & ~(pagesize - 1);
+
+ if (phys)
+ base = virt_to_phys((void *)base);
+
+ size = (info->nr_segments + 1) * sizeof(info->segment[0]);
+ info->segment = xrealloc(info->segment, size);
+ info->segment[info->nr_segments].buf = buf;
+ info->segment[info->nr_segments].bufsz = bufsz;
+ info->segment[info->nr_segments].mem = (void *)base;
+ info->segment[info->nr_segments].memsz = memsz;
+ info->nr_segments++;
+ if (info->nr_segments > KEXEC_MAX_SEGMENTS) {
+ printf("Warning: kernel segment limit reached. "
+ "This will likely fail\n");
+ }
+}
+
+/*
+ * Load the new kernel
+ */
+int kexec_load_file(char *kernel, unsigned long kexec_flags)
+{
+ char *kernel_buf;
+ off_t kernel_size;
+ int i = 0;
+ int result;
+ struct kexec_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.segment = NULL;
+ info.nr_segments = 0;
+ info.entry = NULL;
+ info.kexec_flags = kexec_flags;
+
+ kernel_buf = read_file(kernel, &kernel_size);
+
+ for (i = 0; i < kexec_file_types; i++) {
+ if (kexec_file_type[i].probe(kernel_buf, kernel_size) >= 0)
+ break;
+ }
+
+ if (i == kexec_file_types) {
+ printf("Cannot determine the file type "
+ "of %s\n", kernel);
+ return -1;
+ }
+
+ result = kexec_file_type[i].load(kernel_buf, kernel_size, &info);
+ if (result < 0) {
+ printf("Cannot load %s\n", kernel);
+ return result;
+ }
+
+ /* Verify all of the segments load to a valid location in memory */
+
+ /* Sort the segments and verify we don't have overlaps */
+ if (sort_segments(&info) < 0) {
+ return -1;
+ }
+
+ result = kexec_load(info.entry,
+ info.nr_segments, info.segment, info.kexec_flags);
+
+ return result;
+}
diff --git a/lib/kexec/kexec.h b/lib/kexec/kexec.h
new file mode 100644
index 0000000..381c1e4
--- /dev/null
+++ b/lib/kexec/kexec.h
@@ -0,0 +1,89 @@
+#ifndef KEXEC_H
+#define KEXEC_H
+
+#include "kexec-elf.h"
+
+struct kexec_segment {
+ const void *buf;
+ size_t bufsz;
+ const void *mem;
+ size_t memsz;
+};
+
+struct kexec_info {
+ struct kexec_segment *segment;
+ int nr_segments;
+ void *entry;
+ unsigned long kexec_flags;
+};
+
+typedef int (probe_t)(const char *kernel_buf, off_t kernel_size);
+typedef int (load_t)(const char *kernel_buf, off_t kernel_size,
+ struct kexec_info *info);
+struct kexec_file_type {
+ const char *name;
+ probe_t *probe;
+ load_t *load;
+};
+
+extern struct kexec_file_type kexec_file_type[];
+extern int kexec_file_types;
+
+extern void add_segment(struct kexec_info *info,
+ const void *buf, size_t bufsz, unsigned long base, size_t memsz);
+extern void add_segment_phys_virt(struct kexec_info *info,
+ const void *buf, size_t bufsz, unsigned long base, size_t memsz,
+ int phys);
+
+extern long kexec_load(void *entry, unsigned long nr_segments,
+ struct kexec_segment *segments, unsigned long flags);
+extern int kexec_load_file(char *kernel, unsigned long kexec_flags);
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+/* These values match the ELF architecture values.
+ * Unless there is a good reason that should continue to be the case.
+ */
+#define KEXEC_ARCH_DEFAULT ( 0 << 16)
+#define KEXEC_ARCH_386 ( 3 << 16)
+#define KEXEC_ARCH_X86_64 (62 << 16)
+#define KEXEC_ARCH_PPC (20 << 16)
+#define KEXEC_ARCH_PPC64 (21 << 16)
+#define KEXEC_ARCH_IA_64 (50 << 16)
+#define KEXEC_ARCH_ARM (40 << 16)
+#define KEXEC_ARCH_S390 (22 << 16)
+#define KEXEC_ARCH_SH (42 << 16)
+#define KEXEC_ARCH_MIPS_LE (10 << 16)
+#define KEXEC_ARCH_MIPS ( 8 << 16)
+#define KEXEC_ARCH_CRIS (76 << 16)
+
+#define KEXEC_MAX_SEGMENTS 16
+
+#endif /* KEXEC_H */
--
2.10.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH] spi: mvebu: fix error handling for transfer problems
From: Uwe Kleine-König @ 2016-12-05 10:27 UTC (permalink / raw)
To: barebox, Sebastian Hesselbarth
When a message transfer fails no further messages are transferred, but
the error value was not propagated to the caller.
Fixes: 5db1a578d6ed ("spi: add Marvell MVEBU SoC SPI driver")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
drivers/spi/mvebu_spi.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/mvebu_spi.c b/drivers/spi/mvebu_spi.c
index 335774d4c609..ee7b212962f7 100644
--- a/drivers/spi/mvebu_spi.c
+++ b/drivers/spi/mvebu_spi.c
@@ -305,14 +305,14 @@ static int mvebu_spi_transfer(struct spi_device *spi, struct spi_message *msg)
list_for_each_entry(t, &msg->transfers, transfer_list) {
ret = mvebu_spi_do_transfer(spi, t);
if (ret)
- break;
+ goto err_transfer;
msg->actual_length += t->len;
}
- ret = mvebu_spi_set_cs(priv, spi->chip_select, spi->mode, false);
- if (ret)
- return ret;
+ return mvebu_spi_set_cs(priv, spi->chip_select, spi->mode, false);
+err_transfer:
+ mvebu_spi_set_cs(priv, spi->chip_select, spi->mode, false);
return ret;
}
--
2.10.2
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* Re: [PATCH] bootm: dont use internal oftree fallback by default
From: Alexander Kurz @ 2016-12-05 10:36 UTC (permalink / raw)
To: Lucas Stach; +Cc: barebox
In-Reply-To: <1480671998.17003.37.camel@pengutronix.de>
On Fri, 2 Dec 2016, Lucas Stach wrote:
> Am Donnerstag, den 01.12.2016, 19:55 +0100 schrieb Alexander Kurz:
> > Booting via bootm offers several methods to load oftree data. When no
> > dedicated oftree image is provided, barebox checks for the presence of
> > its own internal oftree, assuming it as a good choice for boot.
> >
> > This fallback method breaks the usecase when a modern OF-based barebox
> > is used to boot a legacy ATAG dependent non OF based vendor provided kernel
> > (e.g. ATAGs will be switched off).
> > Unfortunately those kernels are being still actively shipped today.
> >
> > Change barebox according to the principle of least surprise: when no
> > oftree data is proactively configured, then perform a non-oftree boot.
> > Make the fallback-use of the barebox internal oftree an opt-in feature.
> >
> > Note: this will break boards where the boot process relied on this feature,
> > e.g.: oftree based barebox plus oftree based kernel without an explicit
> > given dts. For those boards global.bootm.internal_oftree_fallback=1 should
> > be set.
> >
> The least surprise on a modern oftree based kernel is that the firmware
> (Barebox) provides the DT, if there isn't an explicit override.
I'm just wondering about the consequences in terms of possible backward
and forward DT compatibility issues. DT can get quite complicated and
often contain parts from the SoC and SoM manufacturer and probably also
the device manufacturer. I am not sure if everyone holds the dicipline
maintaining DT stable like the kernel team does.
Explicitly shipping the kernel-DT with the kernel will prevent unwanted
surprises at this point.
> So NACK on the patch in it's current form. If you have a board where you
> know that the kernel doesn't play along, you may reverse this patch so
> that you can set global.bootm.no_internal_oftree=1 in your board code.
I have already considered it as a proposal-for-discussion.
I'll post the patch with inverted logic later on and let the decision to
you.
Regards, Alexander
>
> Regards,
> Lucas
>
> > Signed-off-by: Alexander Kurz <akurz@blala.de>
> > ---
> > common/bootm.c | 8 ++++++--
> > include/bootm.h | 2 ++
> > 2 files changed, 8 insertions(+), 2 deletions(-)
> >
> > diff --git a/common/bootm.c b/common/bootm.c
> > index 5984319..f5303fa 100644
> > --- a/common/bootm.c
> > +++ b/common/bootm.c
> > @@ -58,6 +58,7 @@ void bootm_data_init_defaults(struct bootm_data *data)
> > data->initrd_address = UIMAGE_INVALID_ADDRESS;
> > data->os_address = UIMAGE_SOME_ADDRESS;
> > data->oftree_file = getenv_nonempty("global.bootm.oftree");
> > + data->internal_oftree_fallback = getenv_nonempty("global.bootm.internal_oftree_fallback");
> > data->os_file = getenv_nonempty("global.bootm.image");
> > getenv_ul("global.bootm.image.loadaddr", &data->os_address);
> > getenv_ul("global.bootm.initrd.loadaddr", &data->initrd_address);
> > @@ -375,14 +376,15 @@ int bootm_load_devicetree(struct image_data *data, unsigned long load_address)
> > pr_err("unable to unflatten devicetree\n");
> > return -EINVAL;
> > }
> > -
> > - } else {
> > + } else if (data->internal_oftree_fallback) {
> > data->of_root_node = of_get_root_node();
> > if (!data->of_root_node)
> > return 0;
> >
> > if (bootm_verbose(data) > 1 && data->of_root_node)
> > printf("using internal devicetree\n");
> > + } else {
> > + return 0;
> > }
> >
> > if (data->initrd_res) {
> > @@ -530,6 +532,7 @@ int bootm_boot(struct bootm_data *bootm_data)
> > data->verify = bootm_data->verify;
> > data->force = bootm_data->force;
> > data->dryrun = bootm_data->dryrun;
> > + data->internal_oftree_fallback = bootm_data->internal_oftree_fallback;
> > data->initrd_address = bootm_data->initrd_address;
> > data->os_address = bootm_data->os_address;
> > data->os_entry = bootm_data->os_entry;
> > @@ -683,6 +686,7 @@ BAREBOX_MAGICVAR_NAMED(global_bootm_image_loadaddr, global.bootm.image.loadaddr,
> > BAREBOX_MAGICVAR_NAMED(global_bootm_initrd, global.bootm.initrd, "bootm default initrd");
> > BAREBOX_MAGICVAR_NAMED(global_bootm_initrd_loadaddr, global.bootm.initrd.loadaddr, "bootm default initrd loadaddr");
> > BAREBOX_MAGICVAR_NAMED(global_bootm_oftree, global.bootm.oftree, "bootm default oftree");
> > +BAREBOX_MAGICVAR_NAMED(global_bootm_internal_oftree_fallback, global.bootm.internal_oftree_fallback, "use barebox oftree as fallback");
> > BAREBOX_MAGICVAR_NAMED(global_bootm_verify, global.bootm.verify, "bootm default verify level");
> > BAREBOX_MAGICVAR_NAMED(global_bootm_verbose, global.bootm.verify, "bootm default verbosity level (0=quiet)");
> > BAREBOX_MAGICVAR_NAMED(global_bootm_appendroot, global.bootm.appendroot, "Add root= option to Kernel to mount rootfs from the device the Kernel comes from");
> > diff --git a/include/bootm.h b/include/bootm.h
> > index 6e9777a..c945c99 100644
> > --- a/include/bootm.h
> > +++ b/include/bootm.h
> > @@ -20,6 +20,7 @@ struct bootm_data {
> > enum bootm_verify verify;
> > bool force;
> > bool dryrun;
> > + bool internal_oftree_fallback;
> > /*
> > * appendroot - if true, try to add a suitable root= Kernel option to
> > * mount the rootfs from the same device as the Kernel comes from.
> > @@ -81,6 +82,7 @@ struct image_data {
> > int verbose;
> > int force;
> > int dryrun;
> > + int internal_oftree_fallback;
> > };
> >
> > struct image_handler {
>
>
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply
* Re: [PATCH] bootm: dont use internal oftree fallback by default
From: Alexander Kurz @ 2016-12-05 10:49 UTC (permalink / raw)
To: Uwe Kleine-König; +Cc: barebox
In-Reply-To: <20161205082919.vbqtltxd43jw4qvj@pengutronix.de>
[-- Attachment #1: Type: TEXT/PLAIN, Size: 2106 bytes --]
On Mon, 5 Dec 2016, Uwe Kleine-König wrote:
> On Fri, Dec 02, 2016 at 10:46:38AM +0100, Lucas Stach wrote:
> > Am Donnerstag, den 01.12.2016, 19:55 +0100 schrieb Alexander Kurz:
> > > Booting via bootm offers several methods to load oftree data. When no
> > > dedicated oftree image is provided, barebox checks for the presence of
> > > its own internal oftree, assuming it as a good choice for boot.
> > >
> > > This fallback method breaks the usecase when a modern OF-based barebox
> > > is used to boot a legacy ATAG dependent non OF based vendor provided kernel
> > > (e.g. ATAGs will be switched off).
> > > Unfortunately those kernels are being still actively shipped today.
> > >
> > > Change barebox according to the principle of least surprise: when no
> > > oftree data is proactively configured, then perform a non-oftree boot.
> > > Make the fallback-use of the barebox internal oftree an opt-in feature.
> > >
> > > Note: this will break boards where the boot process relied on this feature,
> > > e.g.: oftree based barebox plus oftree based kernel without an explicit
> > > given dts. For those boards global.bootm.internal_oftree_fallback=1 should
> > > be set.
> > >
> > The least surprise on a modern oftree based kernel is that the firmware
> > (Barebox) provides the DT, if there isn't an explicit override.
> >
> > So NACK on the patch in it's current form. If you have a board where you
> > know that the kernel doesn't play along, you may reverse this patch so
> > that you can set global.bootm.no_internal_oftree=1 in your board code.
>
> Ack for the NAck. And AFAIK you can already today just do
>
> oftree -f
>
> before calling bootm to discard the internal dtb which should make
> barebox fall back to ATAG.
After flushing the DT, ATAGs get passed during boot,
this makes my patch obsolete now.
Thank you very much,
Alexander
>
> Best regards
> Uwe
>
> --
> Pengutronix e.K. | Uwe Kleine-König |
> Industrial Linux Solutions | http://www.pengutronix.de/ |
>
[-- Attachment #2: Type: text/plain, Size: 149 bytes --]
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply
* [PATCH 00/16] Vybrid related patches
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
Hi everyone,
Here's a second wave of Vybrid related patches that I developed
working with custom Vybrid board.
Most notable features of this patchset are:
- CPU re-clocking for Vybrid
- USB support on Vybrid
- Early pinmux configuration routines for i.MX and Vybrid. An
example of their utility is shown in the patch for i.MX6
SabreSD board (I also have custom code using Vybrid version of
those to read various "bootstraping" pins on my custom design)
Andrey Smirnov (16):
i.MX: esdhc: Enable host->clk during initialization
i.MX: ocotp: Add provisions for storing multiple MAC addresses
i.MX: ocotp: Initialize OCOTP as early as possible
i.MX: ocotp: Initialize 'sense_enable' to true on Vybrid
i.MX: clk: Add IMX_PLLV3_SYS_VF610 subtype
i.MX: vf610: Ramp CPU clock to maximum frequency
i.MX: iomuxv3: Add low-level pad code to headers
i.MX: iomuxv3: Add helper type to deconstruct iomux_v3_cfg_t values
i.MX: iomuxv3: Add low-level pad configuration routine
i.MX6: sabresd: Remove magic numbers in setup_uart
i.MX: iomuxv3: Use helper functions in iomux-v3.h
i.MX: vf610: Add low-level pin configuration helper
i.MX: iomux-vf610: Add missing pad definitions
i.MX: imx-usb-phy: Add VF610 OF compatiblity string
i.MX: Default CONFI_USB_IMX_PHY to 'y' on Vybrid
i.MX: imx-usb-misc: Add Vybrid support
arch/arm/boards/freescale-mx6-sabresd/lowlevel.c | 9 +-
arch/arm/mach-imx/Kconfig | 13 ++
arch/arm/mach-imx/include/mach/iomux-v3.h | 61 ++++++++
arch/arm/mach-imx/include/mach/iomux-vf610.h | 20 +++
arch/arm/mach-imx/ocotp.c | 58 ++++++-
drivers/clk/imx/clk-pllv3.c | 108 +++++++++++++
drivers/clk/imx/clk-vf610.c | 187 ++++++++++++++++++++++-
drivers/clk/imx/clk.h | 5 +
drivers/mci/imx-esdhc.c | 7 +
drivers/pinctrl/imx-iomux-v3.c | 38 +----
drivers/pinctrl/pinctrl-vf610.c | 11 +-
drivers/usb/imx/Kconfig | 2 +-
drivers/usb/imx/imx-usb-misc.c | 28 ++++
drivers/usb/imx/imx-usb-phy.c | 2 +
14 files changed, 496 insertions(+), 53 deletions(-)
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply
* [PATCH 06/16] i.MX: vf610: Ramp CPU clock to maximum frequency
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Mask ROM leaves the CPU running at 264Mhz, so configure the clock tree
to such that CPU runs at maximum supported frequency, based on speed
grading burned into OCOTP fusebox.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/Kconfig | 13 +++
drivers/clk/imx/clk-vf610.c | 187 +++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 198 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index af533ea..1752335 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -750,6 +750,19 @@ config HABV3_IMG_CRT_DER
endif
+config ADJUST_CPU_CLOCK
+ bool "Adjust CPU clock based on its speed grading"
+ select IMX_OCOTP
+ depends on ARCH_VF610
+ default y
+ help
+ Some i.MX SoCs (e. g. Vybrid) are manufactured to have
+ several variants of the same chip different only in maxumum
+ CPU frequency supported. MaskROM on such chips plays it safe
+ and uses the lowest possible frequency. This option
+ configures Barebox to read chip's speed grade information
+ and increase CPU clock to it's highest possible value.
+
endmenu
endif
diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c
index 04cb02f..1cf2b65 100644
--- a/drivers/clk/imx/clk-vf610.c
+++ b/drivers/clk/imx/clk-vf610.c
@@ -16,7 +16,9 @@
#include <of_address.h>
#include <linux/clkdev.h>
#include <linux/clk.h>
+#include <notifier.h>
#include <dt-bindings/clock/vf610-clock.h>
+#include <mach/vf610-regs.h>
#include "clk.h"
@@ -76,6 +78,7 @@
#define PLL6_CTRL (anatop_base + 0xa0)
#define PLL7_CTRL (anatop_base + 0x20)
#define ANA_MISC1 (anatop_base + 0x160)
+#define PLL_LOCK (anatop_base + 0x2c0)
static void __iomem *anatop_base;
static void __iomem *ccm_base;
@@ -188,8 +191,9 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels));
- clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1);
- clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1);
+ clk[VF610_CLK_PLL1] = imx_clk_pllv3_locked(IMX_PLLV3_SYS_VF610, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1, PLL_LOCK, BIT(6));
+ clk[VF610_CLK_PLL2] = imx_clk_pllv3_locked(IMX_PLLV3_SYS_VF610, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1, PLL_LOCK, BIT(5));
+
clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x2);
clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f);
clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3);
@@ -441,3 +445,182 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
CLK_OF_DECLARE(vf610, "fsl,vf610-ccm", vf610_clocks_init);
+
+#ifdef CONFIG_ADJUST_CPU_CLOCK
+
+enum {
+ OCOTP_SPEED_GRADING_OFFSET = (0x4 * sizeof(uint32_t)),
+ OCOTP_SPEED_GRADING_SHIFT = 18,
+ OCOTP_SPEED_GRADING_MASK = 0b1111,
+
+ VF610_SPEED_500 = 0b1110,
+ VF610_SPEED_400 = 0b1001,
+ VF610_SPEED_266 = 0b0001,
+
+ DDRMC_CR117 = 0x01d4,
+ DDRMC_CR117_AXI0_FITYPEREG_SYNC = 0b01 << 16,
+};
+
+static int vf610_switch_cpu_clock_to_500mhz(void)
+{
+ int ret;
+
+ /*
+ * When switching A5 CPU to 500Mhz we expect DDRC to be
+ * clocked by PLL2_PFD2 and the system to be configured in
+ * asynchronous mode.
+ *
+ * We also can't just use default PFD1 output of PLL1 due to
+ * Errata e6235, so we have to re-clock the PLL itself and use
+ * its output to clock the CPU directly.
+ */
+
+ if (clk_get_parent(clk[VF610_CLK_DDR_SEL]) != clk[VF610_CLK_PLL2_PFD2]) {
+ pr_warn("DDRC is clocked by PLL1, can't switch CPU clock");
+ return -EINVAL;
+ }
+
+ ret = clk_set_parent(clk[VF610_CLK_SYS_SEL], clk[VF610_CLK_PLL2_BUS]);
+ if (ret < 0) {
+ pr_crit("Unable to re-parent '%s'\n",
+ clk[VF610_CLK_SYS_SEL]->name);
+ return ret;
+ }
+
+ ret = clk_set_rate(clk[VF610_CLK_PLL1], 500000000);
+ if (ret < 0) {
+ pr_crit("Unable to set %s to 500Mhz %d\n",
+ clk[VF610_CLK_PLL1]->name, ret);
+ return ret;
+ }
+
+ ret = clk_set_parent(clk[VF610_CLK_PLL1_PFD_SEL], clk[VF610_CLK_PLL1_SYS]);
+ if (ret < 0) {
+ pr_crit("Unable to re-parent '%s'\n",
+ clk[VF610_CLK_PLL1_PFD_SEL]->name);
+ return ret;
+ }
+
+ ret = clk_set_parent(clk[VF610_CLK_SYS_SEL], clk[VF610_CLK_PLL1_PFD_SEL]);
+ if (ret < 0) {
+ pr_crit("Unable to re-parent '%s'\n",
+ clk[VF610_CLK_SYS_SEL]->name);
+ return ret;
+ }
+
+ /*
+ * imx_clk_divider has no error path in its set_rate hook
+ */
+ clk_set_rate(clk[VF610_CLK_SYS_BUS], clk_get_rate(clk[VF610_CLK_SYS_SEL]));
+ clk_set_rate(clk[VF610_CLK_PLATFORM_BUS], clk_get_rate(clk[VF610_CLK_SYS_BUS]) / 3);
+
+ return ret;
+}
+
+static int vf610_switch_cpu_clock_to_400mhz(void)
+{
+ int ret;
+ uint32_t cr117;
+ void * __iomem ddrmc = IOMEM(VF610_DDR_BASE_ADDR);
+
+ if (clk_get_parent(clk[VF610_CLK_DDR_SEL]) != clk[VF610_CLK_PLL2_PFD2]) {
+ pr_warn("DDRC is clocked by PLL1, can't switch CPU clock");
+ return -EINVAL;
+ }
+
+ ret = clk_set_parent(clk[VF610_CLK_PLL2_PFD_SEL], clk[VF610_CLK_PLL2_PFD2]);
+ if (ret < 0) {
+ pr_crit("Unable to re-parent '%s'\n",
+ clk[VF610_CLK_PLL2_PFD_SEL]->name);
+ return ret;
+ }
+
+ ret = clk_set_parent(clk[VF610_CLK_SYS_SEL], clk[VF610_CLK_PLL2_PFD_SEL]);
+ if (ret < 0) {
+ pr_crit("Unable to re-parent '%s'\n",
+ clk[VF610_CLK_SYS_SEL]->name);
+ return ret;
+ }
+
+ /*
+ * imx_clk_divider has no error path in its set_rate hook
+ */
+ clk_set_rate(clk[VF610_CLK_SYS_BUS], clk_get_rate(clk[VF610_CLK_SYS_SEL]));
+ clk_set_rate(clk[VF610_CLK_PLATFORM_BUS], clk_get_rate(clk[VF610_CLK_SYS_BUS]) / 3);
+
+ /*
+ * Now that we are running off of the same clock as DDRMC we
+ * shouldn't need to use clock domain corssing FIFO and
+ * asynchronous mode and instead can swithch to sychronous
+ * mode for AXI0 accesses
+ */
+ cr117 = readl(ddrmc + DDRMC_CR117);
+ cr117 |= DDRMC_CR117_AXI0_FITYPEREG_SYNC;
+ writel(cr117, ddrmc + DDRMC_CR117);
+
+ return 0;
+}
+
+static int vf610_switch_cpu_clock(void)
+{
+ int ret;
+ struct device_node *ocotp_node;
+ struct cdev *ocotp;
+ uint32_t speed_grading;
+
+ if (!of_machine_is_compatible("fsl,vf610"))
+ return 0;
+
+ ocotp_node = of_find_compatible_node(NULL, NULL, "fsl,vf610-ocotp");
+ if (!ocotp_node) {
+ pr_err("Unable to find OCOTP DT node\n");
+ return -ENODEV;
+ }
+
+ ocotp = cdev_by_device_node(ocotp_node);
+ if (!ocotp) {
+ pr_err("No OCOTP character device\n");
+ return -ENODEV;
+ }
+
+ ret = cdev_read(ocotp, &speed_grading, sizeof(speed_grading),
+ OCOTP_SPEED_GRADING_OFFSET, 0);
+ if (ret != sizeof(speed_grading)) {
+ pr_err("Failed to read speed grading data\n");
+ return ret < 0 ? ret : -EIO;
+ }
+
+ speed_grading >>= OCOTP_SPEED_GRADING_SHIFT;
+ speed_grading &= OCOTP_SPEED_GRADING_MASK;
+
+ switch (speed_grading) {
+ default:
+ pr_err("Unknown CPU speed grading %x\n", speed_grading);
+ return -EINVAL;
+
+ case VF610_SPEED_266:
+ return 0;
+
+ case VF610_SPEED_500:
+ ret = vf610_switch_cpu_clock_to_500mhz();
+ break;
+
+ case VF610_SPEED_400:
+ ret = vf610_switch_cpu_clock_to_400mhz();
+ break;
+ }
+
+ clock_notifier_call_chain();
+ return ret;
+}
+/*
+ * We can probably gain a bit of a boot speed if we switch CPU clock
+ * earlier, but if we do this we'd need to figure out a way how to
+ * re-adjust the baud rate settings of the UART for DEBUG_LL
+ * functionality, or, accept the fact that it will be unavailbe after
+ * this hook is executed. Both are far from ideal, so a bit slower
+ * boot it is.
+ */
+postconsole_initcall(vf610_switch_cpu_clock);
+
+#endif
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 01/16] i.MX: esdhc: Enable host->clk during initialization
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
drivers/mci/imx-esdhc.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index d72e3f8..40a086b 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -631,6 +631,13 @@ static int fsl_esdhc_probe(struct device_d *dev)
if (IS_ERR(host->clk))
return PTR_ERR(host->clk);
+ ret = clk_enable(host->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock: %s\n",
+ strerror(ret));
+ return ret;
+ }
+
host->dev = dev;
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 03/16] i.MX: ocotp: Initialize OCOTP as early as possible
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
On Vybrid SoC OCOTP module contains speed grading information that is
needed to correctly adjust CPU clock to its maxumum rate, so we need to
have this information handy as early as possible.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/ocotp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/ocotp.c b/arch/arm/mach-imx/ocotp.c
index 9a07922..667555a 100644
--- a/arch/arm/mach-imx/ocotp.c
+++ b/arch/arm/mach-imx/ocotp.c
@@ -614,4 +614,4 @@ static int imx_ocotp_init(void)
return 0;
}
-coredevice_initcall(imx_ocotp_init);
+postcore_initcall(imx_ocotp_init);
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 04/16] i.MX: ocotp: Initialize 'sense_enable' to true on Vybrid
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Reading speed grading information (OCOTP word 0x4) only seem to work
when that data read is straigh out of a fusebox. Allow differenet SoC to
deafalut 'sense_enable' to different values and set it to true on
Vybrid.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/ocotp.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/mach-imx/ocotp.c b/arch/arm/mach-imx/ocotp.c
index 667555a..e9c07c2 100644
--- a/arch/arm/mach-imx/ocotp.c
+++ b/arch/arm/mach-imx/ocotp.c
@@ -79,6 +79,7 @@ struct imx_ocotp_data {
u32 (*addr_to_offset)(u32 addr);
u8 mac_offsets[MAX_MAC_OFFSETS];
u8 mac_offsets_num;
+ bool sense_enable;
};
struct ocotp_priv {
@@ -494,6 +495,8 @@ static int imx_ocotp_probe(struct device_d *dev)
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
+ priv->sense_enable = priv->data->sense_enable;
+
strcpy(priv->dev.name, "ocotp");
priv->dev.parent = dev;
register_device(&priv->dev);
@@ -565,6 +568,7 @@ static struct imx_ocotp_data imx6q_ocotp_data = {
.addr_to_offset = imx6q_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { MAC_OFFSET_0 },
+ .sense_enable = false,
};
static struct imx_ocotp_data imx6sl_ocotp_data = {
@@ -572,6 +576,7 @@ static struct imx_ocotp_data imx6sl_ocotp_data = {
.addr_to_offset = imx6sl_addr_to_offset,
.mac_offsets_num = 1,
.mac_offsets = { MAC_OFFSET_0 },
+ .sense_enable = false,
};
static struct imx_ocotp_data vf610_ocotp_data = {
@@ -579,6 +584,7 @@ static struct imx_ocotp_data vf610_ocotp_data = {
.addr_to_offset = vf610_addr_to_offset,
.mac_offsets_num = 2,
.mac_offsets = { MAC_OFFSET_0, MAC_OFFSET_1 },
+ .sense_enable = true,
};
static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = {
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 02/16] i.MX: ocotp: Add provisions for storing multiple MAC addresses
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
i.MX SoC variants like Vybrid have more than one built-in Ethernet
interface and as a consequence support storing more than one MAC address
in OCOTP module. Add 'mac_idx' variable to allow to select which mac
address is being referred to by 'mac_addr' variable and the code to
handle it appropriately.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/ocotp.c | 50 +++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 46 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-imx/ocotp.c b/arch/arm/mach-imx/ocotp.c
index 68ff0ce..9a07922 100644
--- a/arch/arm/mach-imx/ocotp.c
+++ b/arch/arm/mach-imx/ocotp.c
@@ -69,12 +69,16 @@
/* Other definitions */
#define IMX6_OTP_DATA_ERROR_VAL 0xBADABADA
#define DEF_RELAX 20
-#define MAC_OFFSET (0x22 * 4)
+#define MAC_OFFSET_0 (0x22 * 4)
+#define MAC_OFFSET_1 (0x24 * 4)
+#define MAX_MAC_OFFSETS 2
#define MAC_BYTES 8
struct imx_ocotp_data {
int num_regs;
u32 (*addr_to_offset)(u32 addr);
+ u8 mac_offsets[MAX_MAC_OFFSETS];
+ u8 mac_offsets_num;
};
struct ocotp_priv {
@@ -87,6 +91,7 @@ struct ocotp_priv {
char ethaddr[6];
struct regmap_config map_config;
const struct imx_ocotp_data *data;
+ int mac_offset_idx;
};
static struct ocotp_priv *imx_ocotp;
@@ -402,8 +407,10 @@ static int imx_ocotp_get_mac(struct param_d *param, void *priv)
struct ocotp_priv *ocotp_priv = priv;
char buf[8];
int i, ret;
+ u8 mac_offset;
- ret = regmap_bulk_read(ocotp_priv->map, MAC_OFFSET, buf, MAC_BYTES);
+ mac_offset = ocotp_priv->data->mac_offsets[ocotp_priv->mac_offset_idx];
+ ret = regmap_bulk_read(ocotp_priv->map, mac_offset, buf, MAC_BYTES);
if (ret < 0)
return ret;
@@ -418,18 +425,43 @@ static int imx_ocotp_set_mac(struct param_d *param, void *priv)
struct ocotp_priv *ocotp_priv = priv;
char buf[8];
int i, ret;
+ u8 mac_offset;
+
+ mac_offset = ocotp_priv->data->mac_offsets[ocotp_priv->mac_offset_idx];
for (i = 0; i < 6; i++)
buf[5 - i] = ocotp_priv->ethaddr[i];
buf[6] = 0; buf[7] = 0;
- ret = regmap_bulk_write(ocotp_priv->map, MAC_OFFSET, buf, MAC_BYTES);
+ ret = regmap_bulk_write(ocotp_priv->map, mac_offset, buf, MAC_BYTES);
if (ret < 0)
return ret;
return 0;
}
+static int imx_ocotp_set_mac_idx(struct param_d *param, void *priv)
+{
+ struct ocotp_priv *ocotp_priv = priv;
+ const int min = 0;
+ const int max = ocotp_priv->data->mac_offsets_num - 1;
+ int old, new, ret = 0;
+
+ old = ocotp_priv->mac_offset_idx;
+ new = clamp(old, min, max);
+
+ if (old != new) {
+ dev_err(&ocotp_priv->dev,
+ "%d is out of bounds for '%s', clamping to %d\n",
+ old, param->name, new);
+ ret = -EINVAL;
+ }
+
+ ocotp_priv->mac_offset_idx = new;
+
+ return ret;
+}
+
static struct regmap_bus imx_ocotp_regmap_bus = {
.reg_write = imx_ocotp_reg_write,
.reg_read = imx_ocotp_reg_read,
@@ -486,9 +518,13 @@ static int imx_ocotp_probe(struct device_d *dev)
NULL, NULL, &priv->permanent_write_enable, NULL);
}
- if (IS_ENABLED(CONFIG_NET))
+ if (IS_ENABLED(CONFIG_NET)) {
+ dev_add_param_int(&priv->dev, "mac_idx",
+ imx_ocotp_set_mac_idx, NULL,
+ &priv->mac_offset_idx, "%d", priv);
dev_add_param_mac(&(priv->dev), "mac_addr", imx_ocotp_set_mac,
imx_ocotp_get_mac, priv->ethaddr, priv);
+ }
dev_add_param_bool(&(priv->dev), "sense_enable", NULL, NULL, &priv->sense_enable, priv);
@@ -527,16 +563,22 @@ static u32 vf610_addr_to_offset(u32 addr)
static struct imx_ocotp_data imx6q_ocotp_data = {
.num_regs = 512,
.addr_to_offset = imx6q_addr_to_offset,
+ .mac_offsets_num = 1,
+ .mac_offsets = { MAC_OFFSET_0 },
};
static struct imx_ocotp_data imx6sl_ocotp_data = {
.num_regs = 256,
.addr_to_offset = imx6sl_addr_to_offset,
+ .mac_offsets_num = 1,
+ .mac_offsets = { MAC_OFFSET_0 },
};
static struct imx_ocotp_data vf610_ocotp_data = {
.num_regs = 512,
.addr_to_offset = vf610_addr_to_offset,
+ .mac_offsets_num = 2,
+ .mac_offsets = { MAC_OFFSET_0, MAC_OFFSET_1 },
};
static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = {
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 08/16] i.MX: iomuxv3: Add helper type to deconstruct iomux_v3_cfg_t values
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/include/mach/iomux-v3.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm/mach-imx/include/mach/iomux-v3.h b/arch/arm/mach-imx/include/mach/iomux-v3.h
index 3bf457f..470f774 100644
--- a/arch/arm/mach-imx/include/mach/iomux-v3.h
+++ b/arch/arm/mach-imx/include/mach/iomux-v3.h
@@ -78,6 +78,22 @@ typedef u64 iomux_v3_cfg_t;
((iomux_v3_cfg_t)(_sel_input_ofs) << MUX_SEL_INPUT_OFS_SHIFT) | \
((iomux_v3_cfg_t)(_sel_input) << MUX_SEL_INPUT_SHIFT))
+
+struct iomux_v3_pad_configuration {
+ u64 mux_ctrl_ofs : 12;
+ u64 pad_ctrl_ofs : 12;
+ u64 sel_input_ofs : 12;
+ u64 mux_mode : 5;
+ u64 pad_ctrl : 18;
+ u64 sel_inp : 4;
+ u64 reserved : 1;
+} __packed;
+
+union iomux_v3_pad {
+ iomux_v3_cfg_t raw;
+ struct iomux_v3_pad_configuration cfg;
+};
+
#define NEW_PAD_CTRL(cfg, pad) (((cfg) & ~MUX_PAD_CTRL_MASK) | MUX_PAD_CTRL(pad))
/*
* Use to set PAD control
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 07/16] i.MX: iomuxv3: Add low-level pad code to headers
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Add a basic low-level pad configuration function that can be used to
implement early boot pin configuration code as well as shared with
various iomuxv3 and vf610 drivers.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/include/mach/iomux-v3.h | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/arch/arm/mach-imx/include/mach/iomux-v3.h b/arch/arm/mach-imx/include/mach/iomux-v3.h
index b8cc9af..3bf457f 100644
--- a/arch/arm/mach-imx/include/mach/iomux-v3.h
+++ b/arch/arm/mach-imx/include/mach/iomux-v3.h
@@ -16,6 +16,8 @@
#ifndef __MACH_IOMUX_V3_H__
#define __MACH_IOMUX_V3_H__
+#include <io.h>
+
/*
* build IOMUX_PAD structure
*
@@ -104,6 +106,32 @@ typedef u64 iomux_v3_cfg_t;
#define IOMUX_CONFIG_SION (0x1 << 4)
+#define SHARE_MUX_CONF_REG 0x1
+#define ZERO_OFFSET_VALID 0x2
+
+static inline void iomux_v3_setup_pad(void __iomem *iomux, unsigned int flags,
+ u32 mux_reg, u32 conf_reg, u32 input_reg,
+ u32 mux_val, u32 conf_val, u32 input_val)
+{
+ const bool mux_ok = !!mux_reg || (flags & ZERO_OFFSET_VALID);
+ const bool conf_ok = !!conf_reg;
+ const bool input_ok = !!input_reg;
+
+ if (flags & SHARE_MUX_CONF_REG) {
+ mux_val |= conf_val;
+ } else {
+ if (conf_ok)
+ writel(conf_val, iomux + conf_reg);
+ }
+
+ if (mux_ok)
+ writel(mux_val, iomux + mux_reg);
+
+ if (input_ok)
+ writel(input_val, iomux + input_reg);
+}
+
+
/*
* setups a single pad in the iomuxer
*/
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 05/16] i.MX: clk: Add IMX_PLLV3_SYS_VF610 subtype
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Add IMX_PLLV3_SYS_VF610 subtype to pllv3 code to be able to control and
re-clock PLL1 and PLL2 on Vybrid SoC. This commit also introduces
imx_clk_pllv3_locked which allows the user to create PLLv3 and specify
how it should be polled for "locked" status (used in .set_rate callback)
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
drivers/clk/imx/clk-pllv3.c | 108 ++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/imx/clk.h | 5 ++
2 files changed, 113 insertions(+)
diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c
index 29c0f1c..dd924a4 100644
--- a/drivers/clk/imx/clk-pllv3.c
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -26,6 +26,8 @@
#define PLL_NUM_OFFSET 0x10
#define PLL_DENOM_OFFSET 0x20
+#define SYS_VF610_PLL_OFFSET 0x10
+
#define BM_PLL_POWER (0x1 << 12)
#define BM_PLL_ENABLE (0x1 << 13)
#define BM_PLL_BYPASS (0x1 << 16)
@@ -38,6 +40,8 @@ struct clk_pllv3 {
u32 div_mask;
u32 div_shift;
const char *parent;
+ void __iomem *lock_reg;
+ u32 lock_mask;
};
#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
@@ -279,6 +283,88 @@ static const struct clk_ops clk_pllv3_mlb_ops = {
.disable = clk_pllv3_disable,
};
+static unsigned long clk_pllv3_sys_vf610_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_pllv3 *pll = to_clk_pllv3(clk);
+
+ u32 mfn = readl(pll->base + SYS_VF610_PLL_OFFSET + PLL_NUM_OFFSET);
+ u32 mfd = readl(pll->base + SYS_VF610_PLL_OFFSET + PLL_DENOM_OFFSET);
+ u32 div = (readl(pll->base) & pll->div_mask) ? 22 : 20;
+
+ return (parent_rate * div) + ((parent_rate / mfd) * mfn);
+}
+
+static long clk_pllv3_sys_vf610_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *prate)
+{
+ unsigned long parent_rate = *prate;
+ unsigned long min_rate = parent_rate * 20;
+ unsigned long max_rate = 528000000;
+ u32 mfn, mfd = 1000000;
+ u64 temp64;
+
+ if (rate >= max_rate)
+ return max_rate;
+ else if (rate < min_rate)
+ rate = min_rate;
+
+ temp64 = (u64) (rate - 20 * parent_rate);
+ temp64 *= mfd;
+ do_div(temp64, parent_rate);
+ mfn = temp64;
+
+ return parent_rate * 20 + parent_rate / mfd * mfn;
+}
+
+static int clk_pllv3_sys_vf610_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pllv3 *pll = to_clk_pllv3(clk);
+ unsigned long min_rate = parent_rate * 20;
+ unsigned long max_rate = 528000000;
+ u32 val;
+ u32 mfn, mfd = 1000000;
+ u64 temp64;
+
+ if (rate < min_rate || rate > max_rate)
+ return -EINVAL;
+
+ val = readl(pll->base);
+
+ if (rate == max_rate) {
+ writel(0, pll->base + SYS_VF610_PLL_OFFSET + PLL_NUM_OFFSET);
+ val |= pll->div_mask;
+ writel(val, pll->base);
+
+ return 0;
+ } else {
+ val &= ~pll->div_mask;
+ }
+
+ temp64 = (u64) (rate - 20 * parent_rate);
+ temp64 *= mfd;
+ do_div(temp64, parent_rate);
+ mfn = temp64;
+
+ writel(val, pll->base);
+ writel(mfn, pll->base + SYS_VF610_PLL_OFFSET + PLL_NUM_OFFSET);
+ writel(mfd, pll->base + SYS_VF610_PLL_OFFSET + PLL_DENOM_OFFSET);
+
+ while (!(readl(pll->lock_reg) & pll->lock_mask))
+ ;
+
+ return 0;
+}
+
+static const struct clk_ops clk_pllv3_sys_vf610_ops = {
+ .enable = clk_pllv3_enable,
+ .disable = clk_pllv3_disable,
+ .recalc_rate = clk_pllv3_sys_vf610_recalc_rate,
+ .round_rate = clk_pllv3_sys_vf610_round_rate,
+ .set_rate = clk_pllv3_sys_vf610_set_rate,
+};
+
struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
const char *parent, void __iomem *base,
u32 div_mask)
@@ -290,6 +376,9 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
pll = xzalloc(sizeof(*pll));
switch (type) {
+ case IMX_PLLV3_SYS_VF610:
+ ops = &clk_pllv3_sys_vf610_ops;
+ break;
case IMX_PLLV3_SYS:
ops = &clk_pllv3_sys_ops;
break;
@@ -327,3 +416,22 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
return &pll->clk;
}
+
+struct clk *imx_clk_pllv3_locked(enum imx_pllv3_type type, const char *name,
+ const char *parent, void __iomem *base,
+ u32 div_mask, void __iomem *lock_reg, u32 lock_mask)
+{
+ struct clk *clk;
+ struct clk_pllv3 *pll;
+
+ clk = imx_clk_pllv3(type, name, parent, base, div_mask);
+ if (IS_ERR(clk))
+ return clk;
+
+ pll = to_clk_pllv3(clk);
+
+ pll->lock_reg = lock_reg;
+ pll->lock_mask = lock_mask;
+
+ return clk;
+}
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 970f65c..0b28fb2 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -78,6 +78,7 @@ struct clk *imx_clk_pllv2(const char *name, const char *parent,
enum imx_pllv3_type {
IMX_PLLV3_GENERIC,
IMX_PLLV3_SYS,
+ IMX_PLLV3_SYS_VF610,
IMX_PLLV3_USB,
IMX_PLLV3_USB_VF610,
IMX_PLLV3_AV,
@@ -89,6 +90,10 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
const char *parent, void __iomem *base,
u32 div_mask);
+struct clk *imx_clk_pllv3_locked(enum imx_pllv3_type type, const char *name,
+ const char *parent, void __iomem *base,
+ u32 div_mask, void __iomem *lock_reg, u32 lock_mask);
+
struct clk *imx_clk_pfd(const char *name, const char *parent,
void __iomem *reg, u8 idx);
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 09/16] i.MX: iomuxv3: Add low-level pad configuration routine
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Add low-level pad configuration routine that can be used by early boot
code as well as leveraged by pinmux driver.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/include/mach/iomux-v3.h | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/mach-imx/include/mach/iomux-v3.h b/arch/arm/mach-imx/include/mach/iomux-v3.h
index 470f774..0205ec7 100644
--- a/arch/arm/mach-imx/include/mach/iomux-v3.h
+++ b/arch/arm/mach-imx/include/mach/iomux-v3.h
@@ -147,6 +147,23 @@ static inline void iomux_v3_setup_pad(void __iomem *iomux, unsigned int flags,
writel(input_val, iomux + input_reg);
}
+static inline void imx_setup_pad(void __iomem *iomux, iomux_v3_cfg_t __pad)
+{
+ union iomux_v3_pad pad = { .raw = __pad };
+ uint32_t pad_ctrl;
+
+ pad_ctrl = (pad.cfg.pad_ctrl & NO_PAD_CTRL) ? 0 : pad.cfg.pad_ctrl,
+
+ iomux_v3_setup_pad(iomux, 0,
+ pad.cfg.mux_ctrl_ofs,
+ pad.cfg.pad_ctrl_ofs,
+ pad.cfg.sel_input_ofs,
+ pad.cfg.mux_mode,
+ pad_ctrl,
+ pad.cfg.sel_inp);
+}
+
+
/*
* setups a single pad in the iomuxer
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 16/16] i.MX: imx-usb-misc: Add Vybrid support
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Add code to do usbmisc initialization on VF610 family of SoCs. Based on
analogous code from Linux kernel.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
drivers/usb/imx/imx-usb-misc.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/drivers/usb/imx/imx-usb-misc.c b/drivers/usb/imx/imx-usb-misc.c
index 7c18ca2..9cae440 100644
--- a/drivers/usb/imx/imx-usb-misc.c
+++ b/drivers/usb/imx/imx-usb-misc.c
@@ -422,6 +422,28 @@ static __maybe_unused struct imx_usb_misc_data mx6_data = {
.post_init = mx6_post_init,
};
+#define VF610_OVER_CUR_DIS BIT(7)
+
+static __maybe_unused int vf610_initialize_usb_hw(void __iomem *base, int port,
+ unsigned int flags)
+{
+ u32 reg;
+
+ if (port >= 1)
+ return -EINVAL;
+
+ if (flags & MXC_EHCI_DISABLE_OVERCURRENT) {
+ reg = readl(base);
+ writel(reg | VF610_OVER_CUR_DIS, base);
+ }
+
+ return 0;
+}
+
+static __maybe_unused struct imx_usb_misc_data vf610_data = {
+ .init = vf610_initialize_usb_hw,
+};
+
static struct platform_device_id imx_usbmisc_ids[] = {
#ifdef CONFIG_ARCH_IMX25
{
@@ -519,6 +541,12 @@ static __maybe_unused struct of_device_id imx_usbmisc_dt_ids[] = {
.data = &mx6_data,
},
#endif
+#ifdef CONFIG_ARCH_VF610
+ {
+ .compatible = "fsl,vf610-usbmisc",
+ .data = &vf610_data,
+ },
+#endif
{
/* sentinel */
},
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 11/16] i.MX: iomuxv3: Use helper functions in iomux-v3.h
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Avoid code duplication by using helper functions from iomux-v3.h
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
drivers/pinctrl/imx-iomux-v3.c | 38 ++++----------------------------------
1 file changed, 4 insertions(+), 34 deletions(-)
diff --git a/drivers/pinctrl/imx-iomux-v3.c b/drivers/pinctrl/imx-iomux-v3.c
index 4b3f033..bc93140 100644
--- a/drivers/pinctrl/imx-iomux-v3.c
+++ b/drivers/pinctrl/imx-iomux-v3.c
@@ -31,46 +31,17 @@ struct imx_iomux_v3 {
};
static void __iomem *iomuxv3_base;
-static struct device_d *iomuxv3_dev;
-static void imx_iomuxv3_setup_single(void __iomem *base, struct device_d *dev,
- u32 mux_reg, u32 conf_reg, u32 input_reg,
- u32 mux_val, u32 conf_val, u32 input_val)
-{
- dev_dbg(dev,
- "mux: 0x%08x -> 0x%04x, conf: 0x%08x -> 0x%04x input: 0x%08x -> 0x%04x\n",
- mux_val, mux_reg, conf_val, conf_reg, input_val, input_reg);
-
- if (mux_reg)
- writel(mux_val, base + mux_reg);
- if (conf_reg)
- writel(conf_val, base + conf_reg);
- if (input_reg)
- writel(input_val, base + input_reg);
-}
/*
* configures a single pad in the iomuxer
*/
int mxc_iomux_v3_setup_pad(iomux_v3_cfg_t pad)
{
- u32 mux_reg = (pad & MUX_CTRL_OFS_MASK) >> MUX_CTRL_OFS_SHIFT;
- u32 mux_val = (pad & MUX_MODE_MASK) >> MUX_MODE_SHIFT;
- u32 input_reg = (pad & MUX_SEL_INPUT_OFS_MASK) >> MUX_SEL_INPUT_OFS_SHIFT;
- u32 input_val = (pad & MUX_SEL_INPUT_MASK) >> MUX_SEL_INPUT_SHIFT;
- u32 conf_reg = (pad & MUX_PAD_CTRL_OFS_MASK) >> MUX_PAD_CTRL_OFS_SHIFT;
- u32 conf_val = (pad & MUX_PAD_CTRL_MASK) >> MUX_PAD_CTRL_SHIFT;
-
if (!iomuxv3_base)
return -EINVAL;
- if (conf_val & NO_PAD_CTRL)
- conf_reg = 0;
-
- imx_iomuxv3_setup_single(iomuxv3_base, iomuxv3_dev,
- mux_reg, conf_reg, input_reg,
- mux_val, conf_val, input_val);
-
+ imx_setup_pad(iomuxv3_base, pad);
return 0;
}
EXPORT_SYMBOL(mxc_iomux_v3_setup_pad);
@@ -140,9 +111,9 @@ static int imx_iomux_v3_set_state(struct pinctrl_device *pdev, struct device_nod
if (conf_val & IMX_DT_NO_PAD_CTL)
conf_reg = 0;
- imx_iomuxv3_setup_single(iomux->base, iomux->pinctrl.dev,
- mux_reg, conf_reg, input_reg,
- mux_val, conf_val, input_val);
+ iomux_v3_setup_pad(iomux->base, 0,
+ mux_reg, conf_reg, input_reg,
+ mux_val, conf_val, input_val);
}
return 0;
@@ -183,7 +154,6 @@ static int imx_iomux_v3_probe(struct device_d *dev)
if (IS_ERR(iores))
return PTR_ERR(iores);
iomuxv3_base = IOMEM(iores->start);
- iomuxv3_dev = dev;
if (IS_ENABLED(CONFIG_PINCTRL) && dev->device_node)
ret = imx_pinctrl_dt(dev, iomuxv3_base);
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 10/16] i.MX6: sabresd: Remove magic numbers in setup_uart
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Remove magic numbers in setup_uart and replace them with calls to
iomuxv3 helper functions.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/boards/freescale-mx6-sabresd/lowlevel.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/arch/arm/boards/freescale-mx6-sabresd/lowlevel.c b/arch/arm/boards/freescale-mx6-sabresd/lowlevel.c
index b329f46..5743dbc 100644
--- a/arch/arm/boards/freescale-mx6-sabresd/lowlevel.c
+++ b/arch/arm/boards/freescale-mx6-sabresd/lowlevel.c
@@ -2,6 +2,7 @@
#include <common.h>
#include <linux/sizes.h>
#include <mach/generic.h>
+#include <mach/iomux-mx6.h>
#include <asm/barebox-arm-head.h>
#include <asm/barebox-arm.h>
@@ -11,12 +12,8 @@ static inline void setup_uart(void)
imx6_ungate_all_peripherals();
- writel(0x1b0b1, iomuxbase + 0x0650);
- writel(3, iomuxbase + 0x0280);
-
- writel(0x1b0b1, iomuxbase + 0x0654);
- writel(3, iomuxbase + 0x0284);
- writel(1, iomuxbase + 0x0920);
+ imx_setup_pad(iomuxbase, MX6Q_PAD_CSI0_DAT10__UART1_TXD);
+ imx_setup_pad(iomuxbase, MX6Q_PAD_CSI0_DAT11__UART1_RXD);
imx6_uart_setup_ll();
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 13/16] i.MX: iomux-vf610: Add missing pad definitions
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/include/mach/iomux-vf610.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/mach-imx/include/mach/iomux-vf610.h b/arch/arm/mach-imx/include/mach/iomux-vf610.h
index 087d6f2..b5c7f7f 100644
--- a/arch/arm/mach-imx/include/mach/iomux-vf610.h
+++ b/arch/arm/mach-imx/include/mach/iomux-vf610.h
@@ -163,9 +163,13 @@ enum {
VF610_PAD_PTD22__NF_IO6 = IOMUX_PAD(0x0120, 0x0120, 2, __NA_, 0, VF610_NFC_IO_PAD_CTRL),
VF610_PAD_PTD21__NF_IO5 = IOMUX_PAD(0x0124, 0x0124, 2, __NA_, 0, VF610_NFC_IO_PAD_CTRL),
VF610_PAD_PTD20__NF_IO4 = IOMUX_PAD(0x0128, 0x0128, 2, __NA_, 0, VF610_NFC_IO_PAD_CTRL),
+ VF610_PAD_PTD19__GPIO_75 = IOMUX_PAD(0x012C, 0x012C, 0, __NA_, 0, VF610_GPIO_PAD_CTRL),
VF610_PAD_PTD19__NF_IO3 = IOMUX_PAD(0x012c, 0x012c, 2, __NA_, 0, VF610_NFC_IO_PAD_CTRL),
+ VF610_PAD_PTD18__GPIO_76 = IOMUX_PAD(0x0120, 0x0130, 0, __NA_, 0, VF610_GPIO_PAD_CTRL),
VF610_PAD_PTD18__NF_IO2 = IOMUX_PAD(0x0130, 0x0130, 2, __NA_, 0, VF610_NFC_IO_PAD_CTRL),
+ VF610_PAD_PTD17__GPIO_77 = IOMUX_PAD(0x0134, 0x0134, 0, __NA_, 0, VF610_GPIO_PAD_CTRL),
VF610_PAD_PTD17__NF_IO1 = IOMUX_PAD(0x0134, 0x0134, 2, __NA_, 0, VF610_NFC_IO_PAD_CTRL),
+ VF610_PAD_PTD16__GPIO_78 = IOMUX_PAD(0x0138, 0x0138, 0, __NA_, 0, VF610_GPIO_PAD_CTRL),
VF610_PAD_PTD16__NF_IO0 = IOMUX_PAD(0x0138, 0x0138, 2, __NA_, 0, VF610_NFC_IO_PAD_CTRL),
VF610_PAD_PTB24__NF_WE_B = IOMUX_PAD(0x0178, 0x0178, 5, __NA_, 0, VF610_NFC_CN_PAD_CTRL),
VF610_PAD_PTB25__NF_CE0_B = IOMUX_PAD(0x017c, 0x017c, 5, __NA_, 0, VF610_NFC_CN_PAD_CTRL),
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 12/16] i.MX: vf610: Add low-level pin configuration helper
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Add low-level pin configuration helper for early boot code, and convert
pinctrl driver to use that code as well.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
arch/arm/mach-imx/include/mach/iomux-vf610.h | 16 ++++++++++++++++
drivers/pinctrl/pinctrl-vf610.c | 11 ++++++-----
2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/arch/arm/mach-imx/include/mach/iomux-vf610.h b/arch/arm/mach-imx/include/mach/iomux-vf610.h
index 1535628..087d6f2 100644
--- a/arch/arm/mach-imx/include/mach/iomux-vf610.h
+++ b/arch/arm/mach-imx/include/mach/iomux-vf610.h
@@ -223,4 +223,20 @@ enum {
VF610_PAD_DDR_ODT0__DDR_ODT_1 = IOMUX_PAD(0x02d8, 0x02d8, 0, __NA_, 0, VF610_DDR_PAD_CTRL),
};
+#define PINCTRL_VF610_MUX_SHIFT 20
+
+
+static inline void vf610_setup_pad(void __iomem *iomux, iomux_v3_cfg_t __pad)
+{
+ union iomux_v3_pad pad = { .raw = __pad };
+
+ iomux_v3_setup_pad(iomux, SHARE_MUX_CONF_REG | ZERO_OFFSET_VALID,
+ pad.cfg.mux_ctrl_ofs,
+ pad.cfg.pad_ctrl_ofs,
+ pad.cfg.sel_input_ofs,
+ pad.cfg.mux_mode << PINCTRL_VF610_MUX_SHIFT,
+ pad.cfg.pad_ctrl, pad.cfg.sel_inp);
+}
+
+
#endif /* __IOMUX_VF610_H__ */
diff --git a/drivers/pinctrl/pinctrl-vf610.c b/drivers/pinctrl/pinctrl-vf610.c
index b479bf2..4234263 100644
--- a/drivers/pinctrl/pinctrl-vf610.c
+++ b/drivers/pinctrl/pinctrl-vf610.c
@@ -24,9 +24,10 @@
#include <malloc.h>
#include <gpio.h>
+#include <mach/iomux-vf610.h>
+
enum {
PINCTRL_VF610_MUX_LINE_SIZE = 20,
- PINCTRL_VF610_MUX_SHIFT = 20,
PINCTRL_VF610_IBE = 1 << 0,
PINCTRL_VF610_OBE = 1 << 1,
@@ -60,17 +61,17 @@ static int pinctrl_vf610_set_state(struct pinctrl_device *pdev,
npins = size / PINCTRL_VF610_MUX_LINE_SIZE;
for (i = 0; i < npins; i++) {
+ iomux_v3_cfg_t pad;
u32 mux_reg = be32_to_cpu(*list++);
u32 input_reg = be32_to_cpu(*list++);
u32 mux_val = be32_to_cpu(*list++);
u32 input_val = be32_to_cpu(*list++);
u32 conf_val = be32_to_cpu(*list++);
- writel(mux_val << PINCTRL_VF610_MUX_SHIFT | conf_val,
- iomux->base + mux_reg);
+ pad = IOMUX_PAD(mux_reg, mux_reg, mux_val,
+ input_reg, input_val, conf_val);
- if (input_reg)
- writel(input_val, iomux->base + input_reg);
+ vf610_setup_pad(iomux->base, pad);
}
return 0;
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
* [PATCH 15/16] i.MX: Default CONFI_USB_IMX_PHY to 'y' on Vybrid
From: Andrey Smirnov @ 2016-12-05 14:54 UTC (permalink / raw)
To: barebox; +Cc: Andrey Smirnov
In-Reply-To: <1480949684-18520-1-git-send-email-andrew.smirnov@gmail.com>
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
drivers/usb/imx/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/usb/imx/Kconfig b/drivers/usb/imx/Kconfig
index b0c6a41..8e6f315 100644
--- a/drivers/usb/imx/Kconfig
+++ b/drivers/usb/imx/Kconfig
@@ -16,4 +16,4 @@ config USB_IMX_CHIPIDEA
config USB_IMX_PHY
bool
- default y if ARCH_IMX6 && GENERIC_PHY
+ default y if (ARCH_IMX6 || ARCH_VF610) && GENERIC_PHY
--
2.5.5
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox