1*41173abcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27ca5dc14SFlorian Fainelli /*
37ca5dc14SFlorian Fainelli * Carsten Langgaard, carstenl@mips.com
47ca5dc14SFlorian Fainelli * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
57ca5dc14SFlorian Fainelli *
67ca5dc14SFlorian Fainelli * Putting things on the screen/serial line using YAMONs facilities.
77ca5dc14SFlorian Fainelli */
87ca5dc14SFlorian Fainelli #include <linux/init.h>
97ca5dc14SFlorian Fainelli #include <linux/kernel.h>
107ca5dc14SFlorian Fainelli #include <linux/serial_reg.h>
117ca5dc14SFlorian Fainelli #include <linux/spinlock.h>
1226dd3e4fSPaul Gortmaker #include <linux/export.h>
137ca5dc14SFlorian Fainelli #include <linux/string.h>
147ca5dc14SFlorian Fainelli #include <linux/io.h>
157ca5dc14SFlorian Fainelli #include <asm/bootinfo.h>
165c93316cSAlexander Sverdlin #include <asm/setup.h>
177ca5dc14SFlorian Fainelli
187ca5dc14SFlorian Fainelli #include <asm/mach-ar7/ar7.h>
197ca5dc14SFlorian Fainelli #include <asm/mach-ar7/prom.h>
207ca5dc14SFlorian Fainelli
217ca5dc14SFlorian Fainelli #define MAX_ENTRY 80
227ca5dc14SFlorian Fainelli
237ca5dc14SFlorian Fainelli struct env_var {
247ca5dc14SFlorian Fainelli char *name;
257ca5dc14SFlorian Fainelli char *value;
267ca5dc14SFlorian Fainelli };
277ca5dc14SFlorian Fainelli
287ca5dc14SFlorian Fainelli static struct env_var adam2_env[MAX_ENTRY];
297ca5dc14SFlorian Fainelli
prom_getenv(const char * name)307ca5dc14SFlorian Fainelli char *prom_getenv(const char *name)
317ca5dc14SFlorian Fainelli {
327ca5dc14SFlorian Fainelli int i;
334d1da8c2SAlexander Clouter
347ca5dc14SFlorian Fainelli for (i = 0; (i < MAX_ENTRY) && adam2_env[i].name; i++)
357ca5dc14SFlorian Fainelli if (!strcmp(name, adam2_env[i].name))
367ca5dc14SFlorian Fainelli return adam2_env[i].value;
377ca5dc14SFlorian Fainelli
387ca5dc14SFlorian Fainelli return NULL;
397ca5dc14SFlorian Fainelli }
407ca5dc14SFlorian Fainelli EXPORT_SYMBOL(prom_getenv);
417ca5dc14SFlorian Fainelli
ar7_init_cmdline(int argc,char * argv[])427ca5dc14SFlorian Fainelli static void __init ar7_init_cmdline(int argc, char *argv[])
437ca5dc14SFlorian Fainelli {
449feb8367SYoichi Yuasa int i;
457ca5dc14SFlorian Fainelli
469feb8367SYoichi Yuasa for (i = 1; i < argc; i++) {
479feb8367SYoichi Yuasa strlcat(arcs_cmdline, argv[i], COMMAND_LINE_SIZE);
489feb8367SYoichi Yuasa if (i < (argc - 1))
499feb8367SYoichi Yuasa strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE);
507ca5dc14SFlorian Fainelli }
517ca5dc14SFlorian Fainelli }
527ca5dc14SFlorian Fainelli
537ca5dc14SFlorian Fainelli struct psbl_rec {
547ca5dc14SFlorian Fainelli u32 psbl_size;
557ca5dc14SFlorian Fainelli u32 env_base;
567ca5dc14SFlorian Fainelli u32 env_size;
577ca5dc14SFlorian Fainelli u32 ffs_base;
587ca5dc14SFlorian Fainelli u32 ffs_size;
597ca5dc14SFlorian Fainelli };
607ca5dc14SFlorian Fainelli
61edc9ded1SFlorian Fainelli static const char psp_env_version[] __initconst = "TIENV0.8";
627ca5dc14SFlorian Fainelli
637ca5dc14SFlorian Fainelli struct psp_env_chunk {
647ca5dc14SFlorian Fainelli u8 num;
657ca5dc14SFlorian Fainelli u8 ctrl;
667ca5dc14SFlorian Fainelli u16 csum;
677ca5dc14SFlorian Fainelli u8 len;
687ca5dc14SFlorian Fainelli char data[11];
69c4a50541SFlorian Fainelli } __packed;
707ca5dc14SFlorian Fainelli
717ca5dc14SFlorian Fainelli struct psp_var_map_entry {
727ca5dc14SFlorian Fainelli u8 num;
737ca5dc14SFlorian Fainelli char *value;
747ca5dc14SFlorian Fainelli };
757ca5dc14SFlorian Fainelli
76edc9ded1SFlorian Fainelli static const struct psp_var_map_entry psp_var_map[] = {
777ca5dc14SFlorian Fainelli { 1, "cpufrequency" },
787ca5dc14SFlorian Fainelli { 2, "memsize" },
797ca5dc14SFlorian Fainelli { 3, "flashsize" },
807ca5dc14SFlorian Fainelli { 4, "modetty0" },
817ca5dc14SFlorian Fainelli { 5, "modetty1" },
827ca5dc14SFlorian Fainelli { 8, "maca" },
837ca5dc14SFlorian Fainelli { 9, "macb" },
847ca5dc14SFlorian Fainelli { 28, "sysfrequency" },
857ca5dc14SFlorian Fainelli { 38, "mipsfrequency" },
867ca5dc14SFlorian Fainelli };
877ca5dc14SFlorian Fainelli
887ca5dc14SFlorian Fainelli /*
897ca5dc14SFlorian Fainelli
907ca5dc14SFlorian Fainelli Well-known variable (num is looked up in table above for matching variable name)
917ca5dc14SFlorian Fainelli Example: cpufrequency=211968000
927ca5dc14SFlorian Fainelli +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
937ca5dc14SFlorian Fainelli | 01 |CTRL|CHECKSUM | 01 | _2 | _1 | _1 | _9 | _6 | _8 | _0 | _0 | _0 | \0 | FF
947ca5dc14SFlorian Fainelli +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
957ca5dc14SFlorian Fainelli
967ca5dc14SFlorian Fainelli Name=Value pair in a single chunk
977ca5dc14SFlorian Fainelli Example: NAME=VALUE
987ca5dc14SFlorian Fainelli +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
997ca5dc14SFlorian Fainelli | 00 |CTRL|CHECKSUM | 01 | _N | _A | _M | _E | _0 | _V | _A | _L | _U | _E | \0
1007ca5dc14SFlorian Fainelli +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
1017ca5dc14SFlorian Fainelli
1027ca5dc14SFlorian Fainelli Name=Value pair in 2 chunks (len is the number of chunks)
1037ca5dc14SFlorian Fainelli Example: bootloaderVersion=1.3.7.15
1047ca5dc14SFlorian Fainelli +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
1057ca5dc14SFlorian Fainelli | 00 |CTRL|CHECKSUM | 02 | _b | _o | _o | _t | _l | _o | _a | _d | _e | _r | _V
1067ca5dc14SFlorian Fainelli +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
1077ca5dc14SFlorian Fainelli | _e | _r | _s | _i | _o | _n | \0 | _1 | _. | _3 | _. | _7 | _. | _1 | _5 | \0
1087ca5dc14SFlorian Fainelli +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+---
1097ca5dc14SFlorian Fainelli
1107ca5dc14SFlorian Fainelli Data is padded with 0xFF
1117ca5dc14SFlorian Fainelli
1127ca5dc14SFlorian Fainelli */
1137ca5dc14SFlorian Fainelli
1147ca5dc14SFlorian Fainelli #define PSP_ENV_SIZE 4096
1157ca5dc14SFlorian Fainelli
1167ca5dc14SFlorian Fainelli static char psp_env_data[PSP_ENV_SIZE] = { 0, };
1177ca5dc14SFlorian Fainelli
lookup_psp_var_map(u8 num)1187ca5dc14SFlorian Fainelli static char * __init lookup_psp_var_map(u8 num)
1197ca5dc14SFlorian Fainelli {
1207ca5dc14SFlorian Fainelli int i;
1217ca5dc14SFlorian Fainelli
122ea85a0e4SRoel Kluin for (i = 0; i < ARRAY_SIZE(psp_var_map); i++)
1237ca5dc14SFlorian Fainelli if (psp_var_map[i].num == num)
1247ca5dc14SFlorian Fainelli return psp_var_map[i].value;
1257ca5dc14SFlorian Fainelli
1267ca5dc14SFlorian Fainelli return NULL;
1277ca5dc14SFlorian Fainelli }
1287ca5dc14SFlorian Fainelli
add_adam2_var(char * name,char * value)1297ca5dc14SFlorian Fainelli static void __init add_adam2_var(char *name, char *value)
1307ca5dc14SFlorian Fainelli {
1317ca5dc14SFlorian Fainelli int i;
1324d1da8c2SAlexander Clouter
1337ca5dc14SFlorian Fainelli for (i = 0; i < MAX_ENTRY; i++) {
1347ca5dc14SFlorian Fainelli if (!adam2_env[i].name) {
1357ca5dc14SFlorian Fainelli adam2_env[i].name = name;
1367ca5dc14SFlorian Fainelli adam2_env[i].value = value;
1377ca5dc14SFlorian Fainelli return;
1387ca5dc14SFlorian Fainelli } else if (!strcmp(adam2_env[i].name, name)) {
1397ca5dc14SFlorian Fainelli adam2_env[i].value = value;
1407ca5dc14SFlorian Fainelli return;
1417ca5dc14SFlorian Fainelli }
1427ca5dc14SFlorian Fainelli }
1437ca5dc14SFlorian Fainelli }
1447ca5dc14SFlorian Fainelli
parse_psp_env(void * psp_env_base)1457ca5dc14SFlorian Fainelli static int __init parse_psp_env(void *psp_env_base)
1467ca5dc14SFlorian Fainelli {
1477ca5dc14SFlorian Fainelli int i, n;
1487ca5dc14SFlorian Fainelli char *name, *value;
1497ca5dc14SFlorian Fainelli struct psp_env_chunk *chunks = (struct psp_env_chunk *)psp_env_data;
1507ca5dc14SFlorian Fainelli
1517ca5dc14SFlorian Fainelli memcpy_fromio(chunks, psp_env_base, PSP_ENV_SIZE);
1527ca5dc14SFlorian Fainelli
1537ca5dc14SFlorian Fainelli i = 1;
1547ca5dc14SFlorian Fainelli n = PSP_ENV_SIZE / sizeof(struct psp_env_chunk);
1557ca5dc14SFlorian Fainelli while (i < n) {
1567ca5dc14SFlorian Fainelli if ((chunks[i].num == 0xff) || ((i + chunks[i].len) > n))
1577ca5dc14SFlorian Fainelli break;
1587ca5dc14SFlorian Fainelli value = chunks[i].data;
1597ca5dc14SFlorian Fainelli if (chunks[i].num) {
1607ca5dc14SFlorian Fainelli name = lookup_psp_var_map(chunks[i].num);
1617ca5dc14SFlorian Fainelli } else {
1627ca5dc14SFlorian Fainelli name = value;
1637ca5dc14SFlorian Fainelli value += strlen(name) + 1;
1647ca5dc14SFlorian Fainelli }
1657ca5dc14SFlorian Fainelli if (name)
1667ca5dc14SFlorian Fainelli add_adam2_var(name, value);
1677ca5dc14SFlorian Fainelli i += chunks[i].len;
1687ca5dc14SFlorian Fainelli }
1697ca5dc14SFlorian Fainelli return 0;
1707ca5dc14SFlorian Fainelli }
1717ca5dc14SFlorian Fainelli
ar7_init_env(struct env_var * env)1727ca5dc14SFlorian Fainelli static void __init ar7_init_env(struct env_var *env)
1737ca5dc14SFlorian Fainelli {
1747ca5dc14SFlorian Fainelli int i;
1757ca5dc14SFlorian Fainelli struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300));
1767ca5dc14SFlorian Fainelli void *psp_env = (void *)KSEG1ADDR(psbl->env_base);
1777ca5dc14SFlorian Fainelli
1787ca5dc14SFlorian Fainelli if (strcmp(psp_env, psp_env_version) == 0) {
1797ca5dc14SFlorian Fainelli parse_psp_env(psp_env);
1807ca5dc14SFlorian Fainelli } else {
1817ca5dc14SFlorian Fainelli for (i = 0; i < MAX_ENTRY; i++, env++)
1827ca5dc14SFlorian Fainelli if (env->name)
1837ca5dc14SFlorian Fainelli add_adam2_var(env->name, env->value);
1847ca5dc14SFlorian Fainelli }
1857ca5dc14SFlorian Fainelli }
1867ca5dc14SFlorian Fainelli
console_config(void)1877ca5dc14SFlorian Fainelli static void __init console_config(void)
1887ca5dc14SFlorian Fainelli {
1897ca5dc14SFlorian Fainelli #ifdef CONFIG_SERIAL_8250_CONSOLE
1907ca5dc14SFlorian Fainelli char console_string[40];
1917ca5dc14SFlorian Fainelli int baud = 0;
1927ca5dc14SFlorian Fainelli char parity = '\0', bits = '\0', flow = '\0';
1937ca5dc14SFlorian Fainelli char *s, *p;
1947ca5dc14SFlorian Fainelli
1959fd4c4f4SYoichi Yuasa if (strstr(arcs_cmdline, "console="))
1967ca5dc14SFlorian Fainelli return;
1977ca5dc14SFlorian Fainelli
1987ca5dc14SFlorian Fainelli s = prom_getenv("modetty0");
1997ca5dc14SFlorian Fainelli if (s) {
2007ca5dc14SFlorian Fainelli baud = simple_strtoul(s, &p, 10);
2017ca5dc14SFlorian Fainelli s = p;
2027ca5dc14SFlorian Fainelli if (*s == ',')
2037ca5dc14SFlorian Fainelli s++;
2047ca5dc14SFlorian Fainelli if (*s)
2057ca5dc14SFlorian Fainelli parity = *s++;
2067ca5dc14SFlorian Fainelli if (*s == ',')
2077ca5dc14SFlorian Fainelli s++;
2087ca5dc14SFlorian Fainelli if (*s)
2097ca5dc14SFlorian Fainelli bits = *s++;
2107ca5dc14SFlorian Fainelli if (*s == ',')
2117ca5dc14SFlorian Fainelli s++;
2127ca5dc14SFlorian Fainelli if (*s == 'h')
2137ca5dc14SFlorian Fainelli flow = 'r';
2147ca5dc14SFlorian Fainelli }
2157ca5dc14SFlorian Fainelli
2167ca5dc14SFlorian Fainelli if (baud == 0)
2177ca5dc14SFlorian Fainelli baud = 38400;
2187ca5dc14SFlorian Fainelli if (parity != 'n' && parity != 'o' && parity != 'e')
2197ca5dc14SFlorian Fainelli parity = 'n';
2207ca5dc14SFlorian Fainelli if (bits != '7' && bits != '8')
2217ca5dc14SFlorian Fainelli bits = '8';
2227ca5dc14SFlorian Fainelli
2237ca5dc14SFlorian Fainelli if (flow == 'r')
2247ca5dc14SFlorian Fainelli sprintf(console_string, " console=ttyS0,%d%c%c%c", baud,
2257ca5dc14SFlorian Fainelli parity, bits, flow);
2267ca5dc14SFlorian Fainelli else
2277ca5dc14SFlorian Fainelli sprintf(console_string, " console=ttyS0,%d%c%c", baud, parity,
2287ca5dc14SFlorian Fainelli bits);
2299feb8367SYoichi Yuasa strlcat(arcs_cmdline, console_string, COMMAND_LINE_SIZE);
2307ca5dc14SFlorian Fainelli #endif
2317ca5dc14SFlorian Fainelli }
2327ca5dc14SFlorian Fainelli
prom_init(void)2337ca5dc14SFlorian Fainelli void __init prom_init(void)
2347ca5dc14SFlorian Fainelli {
2357ca5dc14SFlorian Fainelli ar7_init_cmdline(fw_arg0, (char **)fw_arg1);
2367ca5dc14SFlorian Fainelli ar7_init_env((struct env_var *)fw_arg2);
2377ca5dc14SFlorian Fainelli console_config();
2387ca5dc14SFlorian Fainelli }
2397ca5dc14SFlorian Fainelli
2407ca5dc14SFlorian Fainelli #define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4)))
serial_in(int offset)2417ca5dc14SFlorian Fainelli static inline unsigned int serial_in(int offset)
2427ca5dc14SFlorian Fainelli {
2437ca5dc14SFlorian Fainelli return readl((void *)PORT(offset));
2447ca5dc14SFlorian Fainelli }
2457ca5dc14SFlorian Fainelli
serial_out(int offset,int value)2467ca5dc14SFlorian Fainelli static inline void serial_out(int offset, int value)
2477ca5dc14SFlorian Fainelli {
2487ca5dc14SFlorian Fainelli writel(value, (void *)PORT(offset));
2497ca5dc14SFlorian Fainelli }
2507ca5dc14SFlorian Fainelli
prom_putchar(char c)2515c93316cSAlexander Sverdlin void prom_putchar(char c)
2527ca5dc14SFlorian Fainelli {
2537ca5dc14SFlorian Fainelli while ((serial_in(UART_LSR) & UART_LSR_TEMT) == 0)
2547ca5dc14SFlorian Fainelli ;
2557ca5dc14SFlorian Fainelli serial_out(UART_TX, c);
2567ca5dc14SFlorian Fainelli }
257