blob: f0126ead346fa945c28b8e0edd14d70ee4dd9133 [file] [log] [blame]
/*
* Copyright (C) 2009 Broadcom Corporation
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/ctype.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/root_dev.h>
#include <linux/types.h>
#include <linux/smp.h>
#include <linux/bmoca-compat.h>
#include <linux/version.h>
#include <linux/serial_reg.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/compiler.h>
#include <linux/pci.h>
#include <linux/console.h>
#include <linux/mtd/mtd.h>
#include <asm/bootinfo.h>
#include <asm/r4kcache.h>
#include <asm/traps.h>
#include <asm/cacheflush.h>
#include <asm/mipsregs.h>
#include <asm/hazards.h>
#include <asm/smp-ops.h>
#include <asm/reboot.h>
#include <asm/fw/cfe/cfe_api.h>
#include <asm/fw/cfe/cfe_error.h>
#include <linux/brcmstb/brcmstb.h>
#include <spaces.h>
int cfe_lock_console_in = 1;
unsigned long __initdata cfe_seal;
unsigned long __initdata cfe_entry;
unsigned long __initdata cfe_handle;
#ifdef CONFIG_CMDLINE_BOOL
static char __initdata builtin_cmdline[COMMAND_LINE_SIZE] = CONFIG_CMDLINE;
#endif
/***********************************************************************
* CFE bootloader queries
***********************************************************************/
static int __init hex(char ch)
{
if (ch >= 'a' && ch <= 'f')
return ch-'a'+10;
if (ch >= '0' && ch <= '9')
return ch-'0';
if (ch >= 'A' && ch <= 'F')
return ch-'A'+10;
return -1;
}
static int __init hex16(const char *b)
{
int d0, d1;
d0 = hex(b[0]);
d1 = hex(b[1]);
if ((d0 == -1) || (d1 == -1))
return -1;
return (d0 << 4) | d1;
}
void __init cfe_die(char *fmt, ...)
{
char msg[128];
va_list ap;
int handle;
unsigned int count;
va_start(ap, fmt);
vsprintf(msg, fmt, ap);
strcat(msg, "\r\n");
if (cfe_seal != CFE_EPTSEAL)
goto no_cfe;
/* disable XKS01 so that CFE can access the registers */
#if defined(CONFIG_CPU_BMIPS4380)
__write_32bit_c0_register($22, 3,
__read_32bit_c0_register($22, 3) & ~BIT(12));
#elif defined(CONFIG_CPU_BMIPS5000)
__write_32bit_c0_register($22, 5,
__read_32bit_c0_register($22, 5) & ~BIT(8));
#endif
handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE);
if (handle < 0)
goto no_cfe;
cfe_write(handle, msg, strlen(msg));
for (count = 0; count < 0x7fffffff; count++)
mb();
cfe_exit(0, 1);
while (1)
;
no_cfe:
/* probably won't print anywhere useful */
printk(KERN_ERR "%s", msg);
BUG();
va_end(ap);
}
static inline int __init parse_eth0_hwaddr(const char *buf, u8 *out)
{
int i, t;
u8 addr[6];
for (i = 0; i < 6; i++) {
t = hex16(buf);
if (t == -1)
return -1;
addr[i] = t;
buf += 3;
}
memcpy(out, addr, 6);
return 0;
}
static inline int __init parse_eth0_mdio_mode(const char *buf,
unsigned long *val)
{
if (strcmp(buf, "boot") == 0 || strcmp(buf, "0") == 0)
*val = 1;
return 0;
}
static inline int __init parse_ulong(const char *buf, unsigned long *val)
{
char *endp;
unsigned long tmp;
tmp = simple_strtoul(buf, &endp, 0);
if (*endp == 0) {
*val = tmp;
return 0;
}
return -1;
}
static inline int __init parse_hex(const char *buf, unsigned long *val)
{
char *endp;
unsigned long tmp;
tmp = simple_strtoul(buf, &endp, 16);
if (*endp == 0) {
*val = tmp;
return 0;
}
return -1;
}
static inline int __init parse_cmdline(const char *buf, char *dst)
{
strlcpy(dst, buf, COMMAND_LINE_SIZE);
return 0;
}
static inline int __init parse_string(const char *buf, char *dst)
{
strlcpy(dst, buf, CFE_STRING_SIZE);
return 0;
}
static char __initdata cfe_buf[COMMAND_LINE_SIZE];
static void __init __maybe_unused cfe_read_configuration(void)
{
int fetched = 0;
printk(KERN_INFO "Fetching vars from bootloader... ");
if (cfe_seal != CFE_EPTSEAL) {
printk(KERN_CONT "none present, using defaults.\n");
return;
}
#define DPRINTK(...) do { } while (0)
/* #define DPRINTK(...) printk(__VA_ARGS__) */
#define FETCH(name, fn, arg) do { \
if (cfe_getenv(name, cfe_buf, COMMAND_LINE_SIZE) == CFE_OK) { \
DPRINTK("Fetch var '%s' = '%s'\n", name, cfe_buf); \
fn(cfe_buf, arg); \
fetched++; \
} else { \
DPRINTK("Could not fetch var '%s'\n", name); \
} \
} while (0)
FETCH("ETH0_HWADDR", parse_eth0_hwaddr, brcm_eth0_macaddr);
FETCH("ETH0_MDIO_MODE", parse_eth0_mdio_mode, &brcm_eth0_no_mdio);
FETCH("ETH0_PHY", parse_string, brcm_eth0_phy);
FETCH("ETH0_PHYADDR", parse_string, brcm_eth0_phyaddr);
FETCH("ETH0_SPEED", parse_ulong, &brcm_eth0_speed);
memcpy(brcm_moca0_macaddr, brcm_eth0_macaddr, ETH_ALEN);
macaddr_increment(brcm_moca0_macaddr, ETH_ALEN, 1);
FETCH("MOCA0_HWADDR", parse_eth0_hwaddr, brcm_moca0_macaddr);
FETCH("DRAM0_SIZE", parse_ulong, &brcm_dram0_size_mb);
FETCH("DRAM1_SIZE", parse_ulong, &brcm_dram1_size_mb);
FETCH("CFE_BOARDNAME", parse_string, brcm_cfe_boardname);
FETCH("BOOT_FLAGS", parse_cmdline, arcs_cmdline);
FETCH("LINUX_FFS_STARTAD", parse_hex, &brcm_mtd_rootfs_start);
FETCH("LINUX_FFS_SIZE", parse_hex, &brcm_mtd_rootfs_len);
FETCH("LINUX_PART_STARTAD", parse_hex, &brcm_mtd_kernel_start);
FETCH("LINUX_PART_SIZE", parse_hex, &brcm_mtd_kernel_len);
FETCH("OCAP_PART_STARTAD", parse_hex, &brcm_mtd_ocap_start);
FETCH("OCAP_PART_SIZE", parse_hex, &brcm_mtd_ocap_len);
FETCH("FLASH_SIZE", parse_ulong, &brcm_mtd_flash_size_mb);
FETCH("FLASH_TYPE", parse_string, brcm_mtd_flash_type);
printk(KERN_CONT "found %d vars.\n", fetched);
}
/***********************************************************************
* Early printk
***********************************************************************/
static unsigned long brcm_early_uart;
#define UART_REG(x) (brcm_early_uart + ((x) << 2))
#define BAUD 115200
static void __init init_port(void)
{
unsigned int divisor;
BDEV_WR(UART_REG(UART_LCR), 0x3); /* 8n1 */
BDEV_WR(UART_REG(UART_IER), 0); /* no interrupt */
BDEV_WR(UART_REG(UART_FCR), 0); /* no fifo */
BDEV_WR(UART_REG(UART_MCR), 0x3); /* DTR + RTS */
BDEV_SET(UART_REG(UART_LCR), UART_LCR_DLAB);
#if defined(CONFIG_BRCM_IKOS)
/* Reverse-engineer brcm_base_baud0 from the bootloader's setting */
divisor = (BDEV_RD(UART_REG(UART_DLM)) << 8) |
BDEV_RD(UART_REG(UART_DLL));
brcm_base_baud0 = divisor * BAUD;
#endif
divisor = (brcm_base_baud0 + BAUD/2) / BAUD;
BDEV_WR(UART_REG(UART_DLL), divisor & 0xff);
BDEV_WR(UART_REG(UART_DLM), (divisor >> 8) & 0xff);
BDEV_UNSET(UART_REG(UART_LCR), UART_LCR_DLAB);
}
void prom_putchar(char x)
{
while (!(BDEV_RD(UART_REG(UART_LSR)) & UART_LSR_THRE))
;
BDEV_WR(UART_REG(UART_TX), x);
}
int lock_console_in(void) {
return cfe_lock_console_in;
}
static void __init brcm_setup_early_printk(void)
{
char *arg = strstr(arcs_cmdline, "console=");
int dev = CONFIG_BRCM_CONSOLE_DEVICE;
const unsigned long base[] = {
BCHP_UARTA_REG_START, BCHP_UARTB_REG_START,
#ifdef CONFIG_BRCM_HAS_UARTC
BCHP_UARTC_REG_START,
#endif
0, 0,
};
if (strstr(arcs_cmdline, "login=") || strstr(arcs_cmdline, "debug=")) {
cfe_lock_console_in = 0;
}
/*
* quick command line parse to pick the early printk console
* valid formats:
* console=ttyS0,115200
* console=0,115200
*/
while (arg && *arg != '\0' && *arg != ' ') {
if ((*arg >= '0') && (*arg <= '3')) {
dev = *arg - '0';
if (base[dev] == 0)
dev = 0;
break;
}
arg++;
}
brcm_early_uart = base[dev];
init_port();
}
/***********************************************************************
* Main entry point
***********************************************************************/
void __init prom_init(void)
{
char *ptr;
cfe_init(cfe_handle, cfe_entry);
bchip_check_compat();
brcmstb_cpu_setup();
/* default to SATA (where available) or MTD rootfs */
#ifdef CONFIG_BRCM_HAS_SATA
ROOT_DEV = Root_SDA1;
#else
ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 0);
#endif
root_mountflags &= ~MS_RDONLY;
bchip_set_features();
#if defined(CONFIG_BRCM_IKOS_DEBUG)
strcpy(arcs_cmdline, "debug initcall_debug");
#elif !defined(CONFIG_BRCM_IKOS)
cfe_read_configuration();
#endif
brcm_setup_early_printk();
#ifdef CONFIG_CMDLINE_BOOL
#ifdef CONFIG_CMDLINE_OVERRIDE
strlcpy(arcs_cmdline, builtin_cmdline, COMMAND_LINE_SIZE);
#else
if (builtin_cmdline[0]) {
strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE);
strlcat(arcs_cmdline, builtin_cmdline, COMMAND_LINE_SIZE);
}
#endif
#endif
/* provide "ubiroot" alias to reduce typing */
if (strstr(arcs_cmdline, "ubiroot"))
strcat(arcs_cmdline, " ubi.mtd=rootfs rootfstype=ubifs "
"root=ubi0:rootfs");
ptr = strstr(arcs_cmdline, "memc1=");
if (ptr)
brcm_dram1_linux_mb = memparse(ptr + 6, &ptr) >> 20;
printk(KERN_INFO "Options: moca=%d sata=%d pcie=%d usb=%d\n",
brcm_moca_enabled, brcm_sata_enabled,
brcm_pcie_enabled, brcm_usb_enabled);
bchip_early_setup();
board_pinmux_setup();
board_get_ram_size(&brcm_dram0_size_mb, &brcm_dram1_size_mb);
do {
unsigned long dram0_mb = brcm_dram0_size_mb, mb;
mb = min(dram0_mb, BRCM_MAX_LOWER_MB);
dram0_mb -= mb;
add_memory_region(0, mb << 20, BOOT_MEM_RAM);
if (!dram0_mb)
break;
#ifdef CONFIG_BRCM_UPPER_MEMORY
mb = min(dram0_mb, BRCM_MAX_UPPER_MB);
dram0_mb -= mb;
plat_wired_tlb_setup();
add_memory_region(UPPERMEM_START, mb << 20, BOOT_MEM_RAM);
if (!dram0_mb)
break;
#endif
#if defined(CONFIG_HIGHMEM)
add_memory_region(HIGHMEM_START, dram0_mb << 20, BOOT_MEM_RAM);
break;
#endif
/*
* We wound up here because the chip's architecture cannot
* make use of all MEMC0 RAM in Linux. i.e. no suitable
* HIGHMEM or upper memory options are supported by the CPU.
*
* But we can still report the excess memory as a "bonus"
* reserved (bmem) region, so the application can manage it.
*/
mb = brcm_dram0_size_mb - dram0_mb; /* Linux memory */
if (!brcm_dram1_size_mb) {
printk(KERN_INFO "MEMC0 split: %lu MB -> Linux; "
"%lu MB -> extra bmem\n", mb, dram0_mb);
brcm_dram1_size_mb = dram0_mb;
brcm_dram1_start = UPPERMEM_START;
}
} while (0);
#if defined(CONFIG_HIGHMEM) && defined(CONFIG_BRCM_HAS_1GB_MEMC1)
if (brcm_dram1_linux_mb > brcm_dram1_size_mb) {
printk(KERN_WARNING "warning: 'memc1=%luM' exceeds "
"available memory (%lu MB); ignoring\n",
brcm_dram1_linux_mb, brcm_dram1_size_mb);
brcm_dram1_linux_mb = 0;
} else if (brcm_dram1_linux_mb)
add_memory_region(MEMC1_START, brcm_dram1_linux_mb << 20,
BOOT_MEM_RAM);
#else
if (brcm_dram1_linux_mb) {
printk(KERN_WARNING "warning: MEMC1 is not available on this "
"system; ignoring\n");
brcm_dram1_linux_mb = 0;
}
#endif
board_ebase_setup = &bmips_ebase_setup;
register_bmips_smp_ops();
}
/***********************************************************************
* NMI hook
***********************************************************************/
static void (*nmi_fn)(struct pt_regs *);
static int brcm_nmi_notifier(struct notifier_block *nb,
unsigned long val, void *v)
{
nmi_fn(NULL);
return 0;
}
void brcm_set_nmi_handler(void (*fn)(struct pt_regs *))
{
nmi_notifier(brcm_nmi_notifier, 0);
}
EXPORT_SYMBOL(brcm_set_nmi_handler);
/***********************************************************************
* Miscellaneous utility functions
***********************************************************************/
static char brcm_system_type[64];
const char *get_system_type(void)
{
u32 class = BRCM_CHIP_ID();
if (class >> 16 == 0)
class >>= 8;
else
class >>= 12;
snprintf(brcm_system_type, 64, "BCM%04x%02X %s platform",
BRCM_CHIP_ID(), BRCM_CHIP_REV() + 0xa0,
class == 0x35 ? "DTV" :
(class == 0x76 ? "DVD" : "STB"));
return (const char *)brcm_system_type;
}
void __init prom_free_prom_memory(void) {}
static void brcm_machine_restart_mips(char *command)
{
dma_cache_wback_inv(0, ~0);
/* On ARM the argument needs to be const */
brcm_machine_restart(command);
}
void __init plat_mem_setup(void)
{
_machine_restart = brcm_machine_restart_mips;
_machine_halt = brcm_machine_halt;
pm_power_off = brcm_machine_halt;
brcm_wraparound_check();
panic_timeout = 10;
#ifdef CONFIG_PCI
pcibios_plat_setup = brcmstb_pcibios_setup;
#endif
add_preferred_console("ttyS", CONFIG_BRCM_CONSOLE_DEVICE, "115200");
}