NetBSD/sys/arch/evbarm/exynos/exynos_machdep.c
marty 4abb095283 XU4 FDT final checkpoint
step N of N: get rid of baggage by removing whole files.  What's left is in
approximately the same shape as when the FDT update started, that is mostly
broken. What's missing is most of the 76 devices recognized in the dtd. In
other words, This is barely the start of a port.

Next up, gpio then i2c, followed either by straightening out usb or getting
the sdhc driver to work -- both probably require getting the interrupt
combiner to work first. A large chunk of work is left to do on the clocks.
I barely got them attaching to fdt and didn't do anything to take advantage
of the information in the dtd.

None of the other existing drivers, such as they are, properly request gpios,
i2c or clocks, and, of course power domains are off the table.
2015-12-21 04:58:50 +00:00

568 lines
15 KiB
C

/* $NetBSD: exynos_machdep.c,v 1.7 2015/12/21 04:58:50 marty Exp $ */
/*
* Copyright (c) 2014 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Reinoud Zandijk.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: exynos_machdep.c,v 1.7 2015/12/21 04:58:50 marty Exp $");
#include "opt_evbarm_boardtype.h"
#include "opt_exynos.h"
#include "opt_machdep.h"
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_ipkdb.h"
#include "opt_md.h"
#include "opt_sscom.h"
#include "opt_arm_debug.h"
#include "ukbd.h"
#include "arml2cc.h" // RPZ why is it not called opt_l2cc.h?
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/cpu.h>
#include <sys/device.h>
#include <sys/exec.h>
#include <sys/kernel.h>
#include <sys/ksyms.h>
#include <sys/msgbuf.h>
#include <sys/proc.h>
#include <sys/reboot.h>
#include <sys/termios.h>
#include <sys/gpio.h>
#include <uvm/uvm_extern.h>
#include <sys/conf.h>
#include <dev/cons.h>
#include <dev/md.h>
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#ifdef KGDB
#include <sys/kgdb.h>
#endif
#include <machine/bootconfig.h>
#include <arm/armreg.h>
#include <arm/undefined.h>
#include <arm/cortex/pl310_var.h>
#include <arm/arm32/machdep.h>
#include <arm/mainbus/mainbus.h>
#include <arm/samsung/exynos5_reg.h>
#include <arm/samsung/exynos_var.h>
#include <evbarm/include/autoconf.h>
#include <evbarm/exynos/platform.h>
#include <dev/i2c/i2cvar.h>
#include <dev/i2c/ddcreg.h>
#include <dev/usb/ukbdvar.h>
#include <net/if_ether.h>
/* serial console stuff */
#include "sscom.h"
#include "opt_sscom.h"
#include <arm/samsung/sscom_var.h>
#include <arm/samsung/sscom_reg.h>
/* so we can load the device tree. NOTE: This requires the kernel to be
* made into a linux (not netbsd) uboot image.
*/
#include <libfdt.h>
#include <dev/fdt/fdtvar.h>
#define FDT_BUF_SIZE (128*1024)
static uint8_t fdt_data[FDT_BUF_SIZE];
extern const int num_exynos_uarts_entries;
extern const struct sscom_uart_info exynos_uarts[];
#ifndef CONSPEED
#define CONSPEED 115200
#endif /* CONSPEED */
#ifndef CONMODE
#define CONMODE ((TTYDEF_CFLAG & ~(CSIZE | CSTOPB | PARENB | HUPCL)) | CS8) /* 8N1 */
#endif /* CONMODE */
static const int conspeed = CONSPEED;
static const int conmode = CONMODE;
/*
* uboot passes 4 arguments to us.
*
* arg0 arg1 arg2 arg3 : the `bootargs' environment variable from the uboot
* context (in PA!)
*
* Note that the storage has to be in .data and not in .bss. On kernel start
* the .bss is cleared and this information would get lost.
*/
uintptr_t uboot_args[4] = { 0 };
/*
* argument and boot configure storage
*/
BootConfig bootconfig; /* for pmap's sake */
char bootargs[MAX_BOOT_STRING] = ""; /* copied string from uboot */
char *boot_args = NULL; /* MI bootargs */
char *boot_file = NULL; /* MI bootfile */
uint8_t uboot_enaddr[ETHER_ADDR_LEN] = {};
void
odroid_device_register(device_t self, void *aux);
/*
* kernel start and end from the linker
*/
extern char KERNEL_BASE_phys[]; /* physical start of kernel */
extern char _end[]; /* physical end of kernel */
#define KERNEL_BASE_PHYS ((paddr_t)KERNEL_BASE_phys)
#define EXYNOS_IOPHYSTOVIRT(a) \
((vaddr_t)(((a) - EXYNOS_CORE_PBASE) + EXYNOS_CORE_VBASE))
static void exynos_reset(void);
static void exynos_powerdown(void);
/* XXX we have no framebuffer implementation yet so com is console XXX */
int use_fb_console = false;
/* prototypes */
void consinit(void);
#ifdef KGDB
static void kgdb_port_init(void);
#endif
static void exynos_extract_mac_adress(void);
void exynos_device_register(device_t self, void *aux);
void exynos_device_register_post_config(device_t self, void *aux);
/*
* Our static device mappings at fixed virtual addresses so we can use them
* while booting the kernel.
*
* Map the extents segment-aligned and segment-rounded in size to avoid L2
* page tables
*/
#define _A(a) ((a) & ~L1_S_OFFSET)
#define _S(s) (((s) + L1_S_SIZE - 1) & (~(L1_S_SIZE-1)))
static const struct pmap_devmap e5_devmap[] = {
{
/* map in core IO space */
.pd_va = _A(EXYNOS_CORE_VBASE),
.pd_pa = _A(EXYNOS_CORE_PBASE),
.pd_size = _S(EXYNOS5_CORE_SIZE),
.pd_prot = VM_PROT_READ | VM_PROT_WRITE,
.pd_cache = PTE_NOCACHE
},
{
/* map in audiocore IO space */
.pd_va = _A(EXYNOS5_AUDIOCORE_VBASE),
.pd_pa = _A(EXYNOS5_AUDIOCORE_PBASE),
.pd_size = _S(EXYNOS5_AUDIOCORE_SIZE),
.pd_prot = VM_PROT_READ | VM_PROT_WRITE,
.pd_cache = PTE_NOCACHE
},
{0}
};
#undef _A
#undef _S
#ifdef PMAP_NEED_ALLOC_POOLPAGE
static struct boot_physmem bp_highgig = {
.bp_pages = (KERNEL_VM_BASE - KERNEL_BASE) / NBPG,
.bp_freelist = VM_FREELIST_ISADMA,
.bp_flags = 0,
};
#endif
static struct gpio_pin_entry {
const char *pin_name;
const char *pin_user;
} gpio_pin_entries[] = {
/* mux@13400000 (muxa) */
{ "gpx3-7", "hdmi-hpd-irq"},
{ "gpx3-6", "hdmi_cec" },
{ "gpx0-7", "dp_hpd_gpio" },
{ "gpx0-4", "pmic-irq" },
{ "gpx3-2", "audio-irq" },
{ "gpx3-4", "b-sess1-irq" },
{ "gpx3-5", "b-sess0-irq" },
{ "gpx1-1", "id2-irq" },
/* mux@134100000 (muxb) */
{ "gpc0-0", "sd0-clk" },
{ "gpc0-1", "sd0-cmd" },
{ "gpc0-7", "sd0-rdqs" },
{ "gpd1-3", "sd0-qrdy" },
{ "gpc0-3", "sd0-bus-width1" },
{ "gpc0-3", "sd0-bus-width4-bit1" },
{ "gpc0-4", "sd0-bus-width4-bit2" },
{ "gpc0-5", "sd0-bus-width4-bit3" },
{ "gpc0-6", "sd0-bus-width4-bit4" },
{ "gpc1-0", "sd1-clk" },
{ "gpc1-1", "sd1-cmd" },
{ "gpc1-3", "sd1-bus-width1" },
{ "gpc1-3", "sd1-bus-width4-bit1" },
{ "gpc1-4", "sd1-bus-width4-bit2" },
{ "gpc1-5", "sd1-bus-width4-bit3" },
{ "gpc1-6", "sd1-bus-width4-bit4" },
/* TODO: muxc and muxd as needed */
{ 0, 0}
};
#ifdef VERBOSE_INIT_ARM
extern void exynos_putchar(int);
static void
exynos_putstr(const char *s)
{
for (const char *p = s; *p; p++) {
exynos_putchar(*p);
}
}
static void
exynos_printn(u_int n, int base)
{
char *p, buf[(sizeof(u_int) * NBBY / 3) + 1 + 2 /* ALT + SIGN */];
p = buf;
do {
*p++ = hexdigits[n % base];
} while (n /= base);
do {
exynos_putchar(*--p);
} while (p > buf);
}
#define DPRINTF(...) printf(__VA_ARGS__)
#define DPRINT(x) exynos_putstr(x)
#define DPRINTN(x,b) exynos_printn((x), (b))
#else
#define DPRINTF(...)
#define DPRINT(x)
#define DPRINTN(x,b)
#endif
extern void cortex_mpstart(void);
/*
* void init_gpio_dictionary(...)
*
* Setup the dictionary of gpio pin names for the drivers to use
*/
static void init_gpio_dictionary(struct gpio_pin_entry *pins,
prop_dictionary_t dict)
{
while (pins->pin_name) {
prop_dictionary_set_cstring(dict, pins->pin_user,
pins->pin_name);
pins++;
}
}
/*
* u_int initarm(...)
*
* Our entry point from the assembly before main() is called.
* - take a copy of the config we got from uboot
* - init the physical console
* - setting up page tables for the kernel
*/
u_int
initarm(void *arg)
{
const struct pmap_devmap const *devmap;
bus_addr_t rambase;
psize_t ram_size;
DPRINT("initarm:");
DPRINT(" mpstart<0x");
DPRINTN((uint32_t)cortex_mpstart, 16);
DPRINT(">");
/* allocate/map our basic memory mapping */
switch (EXYNOS_PRODUCT_FAMILY(exynos_soc_id)) {
#if defined(EXYNOS5)
case EXYNOS5_PRODUCT_FAMILY:
devmap = e5_devmap;
rambase = EXYNOS5_SDRAM_PBASE;
break;
#endif
default:
/* Won't work, but... */
panic("Unknown product family %llx",
EXYNOS_PRODUCT_FAMILY(exynos_soc_id));
}
pmap_devmap_register(devmap);
/* bootstrap soc. uart_address is determined in exynos_start */
paddr_t uart_address = armreg_tpidruro_read();
exynos_bootstrap(EXYNOS_CORE_VBASE, EXYNOS_IOPHYSTOVIRT(uart_address));
/* set up CPU / MMU / TLB functions */
if (set_cpufuncs())
panic("cpu not recognized!");
/* get normal console working */
consinit();
#ifdef KGDB
kgdb_port_init();
#endif
#ifdef VERBOSE_INIT_ARM
printf("\nuboot arg = %#"PRIxPTR", %#"PRIxPTR", %#"PRIxPTR", %#"PRIxPTR"\n",
uboot_args[0], uboot_args[1], uboot_args[2], uboot_args[3]);
printf("Exynos SoC ID %08x\n", exynos_soc_id);
printf("initarm: cbar=%#x\n", armreg_cbar_read());
#endif
/* determine cpu0 clock rate */
exynos_clocks_bootstrap();
#ifdef VERBOSE_INIT_ARM
printf("CPU0 now running on %"PRIu64" Mhz\n", exynos_get_cpufreq()/(1000*1000));
#endif
cpu_reset_address = exynos_reset;
cpu_powerdown_address = exynos_powerdown;
#ifdef VERBOSE_INIT_ARM
printf("\nNetBSD/evbarm (Exynnos 5422) booting ...\n");
#endif
#ifdef BOOT_ARGS
char mi_bootargs[] = BOOT_ARGS;
parse_mi_bootargs(mi_bootargs);
#endif
boot_args = bootargs;
parse_mi_bootargs(boot_args);
exynos_extract_mac_adress();
/* Don't map the DMA reserved region */
// ram_size = (psize_t) 0xC0000000 - 0x40000000;
ram_size = (psize_t) 0xb0000000 - 0x40000000;
#ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS
const bool mapallmem_p = true;
#ifndef PMAP_NEED_ALLOC_POOLPAGE
if (ram_size > KERNEL_VM_BASE - KERNEL_BASE) {
printf("%s: dropping RAM size from %luMB to %uMB\n",
__func__, (unsigned long) (ram_size >> 20),
(KERNEL_VM_BASE - KERNEL_BASE) >> 20);
ram_size = KERNEL_VM_BASE - KERNEL_BASE;
}
#endif
#else
const bool mapallmem_p = false;
#endif
/* Load the dtb */
const uint8_t *fdt_addr_r = (const uint8_t *)uboot_args[2];
printf("fdt addr 0x%08x\n", (uint)fdt_addr_r);
int error = fdt_check_header(fdt_addr_r);
printf("fdt check header returns %d\n", error);
if (error == 0) {
error = fdt_move(fdt_addr_r, fdt_data, sizeof(fdt_data));
printf("fdt move returns %d\n", error);
if (error != 0) {
panic("fdt_move failed: %s", fdt_strerror(error));
}
fdtbus_set_data(fdt_data);
} else {
panic("fdt_check_header failed: %s", fdt_strerror(error));
}
/* Fake bootconfig structure for the benefit of pmap.c. */
bootconfig.dramblocks = 1;
bootconfig.dram[0].address = rambase;
bootconfig.dram[0].pages = ram_size / PAGE_SIZE;
KASSERT((armreg_pfr1_read() & ARM_PFR1_SEC_MASK) != 0);
arm32_bootmem_init(bootconfig.dram[0].address, ram_size,
KERNEL_BASE_PHYS);
arm32_kernel_vm_init(KERNEL_VM_BASE, ARM_VECTORS_LOW, 0, devmap,
mapallmem_p);
/* we've a specific device_register routine */
evbarm_device_register = odroid_device_register;
// evbarm_device_register_post_config = exynos_device_register_post_config;
/*
* If we couldn't map all of memory via TTBR1, limit the memory the
* kernel can allocate from to be from the highest available 1GB.
*/
#ifdef PMAP_NEED_ALLOC_POOLPAGE
if (atop(ram_size) > bp_highgig.bp_pages) {
arm_poolpage_vmfreelist = bp_highgig.bp_freelist;
return initarm_common(KERNEL_VM_BASE, KERNEL_VM_SIZE,
&bp_highgig, 1);
}
#endif
return initarm_common(KERNEL_VM_BASE, KERNEL_VM_SIZE, NULL, 0);
}
void
consinit(void)
{
static bool consinit_called;
if (consinit_called)
return;
consinit_called = true;
#if NSSCOM > 0
bus_space_tag_t bst = &armv7_generic_bs_tag;
bus_addr_t iobase = armreg_tpidruro_read();
bus_space_handle_t bsh = EXYNOS_IOPHYSTOVIRT(iobase);
u_int i;
/*
* No need to guess at the UART frequency since we can calculate it.
*/
uint32_t freq = conspeed
* (16 * (bus_space_read_4(bst, bsh, SSCOM_UBRDIV) + 1)
+ bus_space_read_4(bst, bsh, SSCOM_UFRACVAL));
freq = (freq + conspeed / 2) / 1000;
freq *= 1000;
/* go through all entries */
for (i = 0; i < num_exynos_uarts_entries; i++) {
/* attach console */
if (exynos_uarts[i].iobase + EXYNOS_CORE_PBASE == iobase)
break;
}
KASSERT(i < num_exynos_uarts_entries);
printf("%s: attaching console @ %#"PRIxPTR" (%u HZ, %u bps)\n",
__func__, iobase, freq, conspeed);
if (sscom_cnattach(bst, exynos_core_bsh, &exynos_uarts[i],
conspeed, freq, conmode))
panic("Serial console can not be initialized");
#ifdef VERBOSE_INIT_ARM
printf("Console initialized\n");
#endif
#else
#error only serial console is supported
#if NUKBD > 0
/* allow USB keyboards to become console */
ukbd_cnattach();
#endif /* NUKBD */
#endif
}
/* extract ethernet mac address from bootargs */
static void
exynos_extract_mac_adress(void)
{
char *str, *ptr;
int i, v1, v2, val;
#define EXPECT_COLON() {\
v1 = *ptr++; \
if (v1 != ':') break; \
}
#define EXPECT_HEX(v) {\
(v) = (v) >= '0' && (v) <= '9'? (v) - '0' : \
(v) >= 'a' && (v) <= 'f'? (v) - 'a' + 10 : \
(v) >= 'A' && (v) <= 'F'? (v) - 'A' + 10 : -1; \
if ((v) < 0) break; \
}
#define EXPECT_2HEX(val) {\
v1 = *ptr++; EXPECT_HEX(v1); \
v2 = *ptr++; EXPECT_HEX(v2); \
val = (v1 << 4) | v2; \
}
if (get_bootconf_option(boot_args, "ethaddr",
BOOTOPT_TYPE_STRING, &str)) {
for (i = 0, ptr = str; i < sizeof(uboot_enaddr); i++) {
if (i)
EXPECT_COLON();
EXPECT_2HEX(val);
uboot_enaddr[i] = val;
}
if (i != sizeof(uboot_enaddr)) {
printf( "Ignoring invalid MAC address '%s' passed "
"as boot paramter `ethaddr'\n", str);
memset((char *) uboot_enaddr, 0, sizeof(uboot_enaddr));
}
}
#undef EXPECT_2HEX
#undef EXPECT_HEX
#undef EXPECT_COLON
}
void
odroid_device_register(device_t self, void *aux)
{
prop_dictionary_t dict = device_properties(self);
exynos_device_register(self, aux);
if (device_is_a(self, "exyogpio")) {
init_gpio_dictionary(gpio_pin_entries, dict);
} else if (device_is_a(self, "exyowdt")) {
prop_dictionary_set_uint32(dict, "frequency",
EXYNOS_F_IN_FREQ);
}
}
/*
* Exynos specific tweaks
*/
/*
* The external USB devices are clocked trough the DEBUG clkout
* XXX is this Odroid specific? XXX
*/
void
exynos_init_clkout_for_usb(void)
{
/* Select XUSBXTI as source for CLKOUT */
bus_space_write_4(&armv7_generic_bs_tag, exynos_pmu_bsh,
EXYNOS_PMU_DEBUG_CLKOUT, 0x1000);
}
static void
exynos_reset(void)
{
}
static void
exynos_powerdown(void)
{
}