162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Carsten Langgaard, carstenl@mips.com 462306a36Sopenharmony_ci * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Putting things on the screen/serial line using YAMONs facilities. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/serial_reg.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <asm/bootinfo.h> 1662306a36Sopenharmony_ci#include <asm/setup.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <asm/mach-ar7/ar7.h> 1962306a36Sopenharmony_ci#include <asm/mach-ar7/prom.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MAX_ENTRY 80 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct env_var { 2462306a36Sopenharmony_ci char *name; 2562306a36Sopenharmony_ci char *value; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct env_var adam2_env[MAX_ENTRY]; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cichar *prom_getenv(const char *name) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int i; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci for (i = 0; (i < MAX_ENTRY) && adam2_env[i].name; i++) 3562306a36Sopenharmony_ci if (!strcmp(name, adam2_env[i].name)) 3662306a36Sopenharmony_ci return adam2_env[i].value; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return NULL; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ciEXPORT_SYMBOL(prom_getenv); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void __init ar7_init_cmdline(int argc, char *argv[]) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci int i; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci for (i = 1; i < argc; i++) { 4762306a36Sopenharmony_ci strlcat(arcs_cmdline, argv[i], COMMAND_LINE_SIZE); 4862306a36Sopenharmony_ci if (i < (argc - 1)) 4962306a36Sopenharmony_ci strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct psbl_rec { 5462306a36Sopenharmony_ci u32 psbl_size; 5562306a36Sopenharmony_ci u32 env_base; 5662306a36Sopenharmony_ci u32 env_size; 5762306a36Sopenharmony_ci u32 ffs_base; 5862306a36Sopenharmony_ci u32 ffs_size; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const char psp_env_version[] __initconst = "TIENV0.8"; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct psp_env_chunk { 6462306a36Sopenharmony_ci u8 num; 6562306a36Sopenharmony_ci u8 ctrl; 6662306a36Sopenharmony_ci u16 csum; 6762306a36Sopenharmony_ci u8 len; 6862306a36Sopenharmony_ci char data[11]; 6962306a36Sopenharmony_ci} __packed; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistruct psp_var_map_entry { 7262306a36Sopenharmony_ci u8 num; 7362306a36Sopenharmony_ci char *value; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic const struct psp_var_map_entry psp_var_map[] = { 7762306a36Sopenharmony_ci { 1, "cpufrequency" }, 7862306a36Sopenharmony_ci { 2, "memsize" }, 7962306a36Sopenharmony_ci { 3, "flashsize" }, 8062306a36Sopenharmony_ci { 4, "modetty0" }, 8162306a36Sopenharmony_ci { 5, "modetty1" }, 8262306a36Sopenharmony_ci { 8, "maca" }, 8362306a36Sopenharmony_ci { 9, "macb" }, 8462306a36Sopenharmony_ci { 28, "sysfrequency" }, 8562306a36Sopenharmony_ci { 38, "mipsfrequency" }, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ciWell-known variable (num is looked up in table above for matching variable name) 9162306a36Sopenharmony_ciExample: cpufrequency=211968000 9262306a36Sopenharmony_ci+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 9362306a36Sopenharmony_ci| 01 |CTRL|CHECKSUM | 01 | _2 | _1 | _1 | _9 | _6 | _8 | _0 | _0 | _0 | \0 | FF 9462306a36Sopenharmony_ci+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciName=Value pair in a single chunk 9762306a36Sopenharmony_ciExample: NAME=VALUE 9862306a36Sopenharmony_ci+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 9962306a36Sopenharmony_ci| 00 |CTRL|CHECKSUM | 01 | _N | _A | _M | _E | _0 | _V | _A | _L | _U | _E | \0 10062306a36Sopenharmony_ci+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciName=Value pair in 2 chunks (len is the number of chunks) 10362306a36Sopenharmony_ciExample: bootloaderVersion=1.3.7.15 10462306a36Sopenharmony_ci+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 10562306a36Sopenharmony_ci| 00 |CTRL|CHECKSUM | 02 | _b | _o | _o | _t | _l | _o | _a | _d | _e | _r | _V 10662306a36Sopenharmony_ci+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 10762306a36Sopenharmony_ci| _e | _r | _s | _i | _o | _n | \0 | _1 | _. | _3 | _. | _7 | _. | _1 | _5 | \0 10862306a36Sopenharmony_ci+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciData is padded with 0xFF 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci*/ 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define PSP_ENV_SIZE 4096 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic char psp_env_data[PSP_ENV_SIZE] = { 0, }; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic char * __init lookup_psp_var_map(u8 num) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci int i; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(psp_var_map); i++) 12362306a36Sopenharmony_ci if (psp_var_map[i].num == num) 12462306a36Sopenharmony_ci return psp_var_map[i].value; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return NULL; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void __init add_adam2_var(char *name, char *value) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci int i; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < MAX_ENTRY; i++) { 13462306a36Sopenharmony_ci if (!adam2_env[i].name) { 13562306a36Sopenharmony_ci adam2_env[i].name = name; 13662306a36Sopenharmony_ci adam2_env[i].value = value; 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci } else if (!strcmp(adam2_env[i].name, name)) { 13962306a36Sopenharmony_ci adam2_env[i].value = value; 14062306a36Sopenharmony_ci return; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int __init parse_psp_env(void *psp_env_base) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int i, n; 14862306a36Sopenharmony_ci char *name, *value; 14962306a36Sopenharmony_ci struct psp_env_chunk *chunks = (struct psp_env_chunk *)psp_env_data; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci memcpy_fromio(chunks, psp_env_base, PSP_ENV_SIZE); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci i = 1; 15462306a36Sopenharmony_ci n = PSP_ENV_SIZE / sizeof(struct psp_env_chunk); 15562306a36Sopenharmony_ci while (i < n) { 15662306a36Sopenharmony_ci if ((chunks[i].num == 0xff) || ((i + chunks[i].len) > n)) 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci value = chunks[i].data; 15962306a36Sopenharmony_ci if (chunks[i].num) { 16062306a36Sopenharmony_ci name = lookup_psp_var_map(chunks[i].num); 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci name = value; 16362306a36Sopenharmony_ci value += strlen(name) + 1; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci if (name) 16662306a36Sopenharmony_ci add_adam2_var(name, value); 16762306a36Sopenharmony_ci i += chunks[i].len; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void __init ar7_init_env(struct env_var *env) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int i; 17562306a36Sopenharmony_ci struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300)); 17662306a36Sopenharmony_ci void *psp_env = (void *)KSEG1ADDR(psbl->env_base); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (strcmp(psp_env, psp_env_version) == 0) { 17962306a36Sopenharmony_ci parse_psp_env(psp_env); 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci for (i = 0; i < MAX_ENTRY; i++, env++) 18262306a36Sopenharmony_ci if (env->name) 18362306a36Sopenharmony_ci add_adam2_var(env->name, env->value); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void __init console_config(void) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci#ifdef CONFIG_SERIAL_8250_CONSOLE 19062306a36Sopenharmony_ci char console_string[40]; 19162306a36Sopenharmony_ci int baud = 0; 19262306a36Sopenharmony_ci char parity = '\0', bits = '\0', flow = '\0'; 19362306a36Sopenharmony_ci char *s, *p; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (strstr(arcs_cmdline, "console=")) 19662306a36Sopenharmony_ci return; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci s = prom_getenv("modetty0"); 19962306a36Sopenharmony_ci if (s) { 20062306a36Sopenharmony_ci baud = simple_strtoul(s, &p, 10); 20162306a36Sopenharmony_ci s = p; 20262306a36Sopenharmony_ci if (*s == ',') 20362306a36Sopenharmony_ci s++; 20462306a36Sopenharmony_ci if (*s) 20562306a36Sopenharmony_ci parity = *s++; 20662306a36Sopenharmony_ci if (*s == ',') 20762306a36Sopenharmony_ci s++; 20862306a36Sopenharmony_ci if (*s) 20962306a36Sopenharmony_ci bits = *s++; 21062306a36Sopenharmony_ci if (*s == ',') 21162306a36Sopenharmony_ci s++; 21262306a36Sopenharmony_ci if (*s == 'h') 21362306a36Sopenharmony_ci flow = 'r'; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (baud == 0) 21762306a36Sopenharmony_ci baud = 38400; 21862306a36Sopenharmony_ci if (parity != 'n' && parity != 'o' && parity != 'e') 21962306a36Sopenharmony_ci parity = 'n'; 22062306a36Sopenharmony_ci if (bits != '7' && bits != '8') 22162306a36Sopenharmony_ci bits = '8'; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (flow == 'r') 22462306a36Sopenharmony_ci sprintf(console_string, " console=ttyS0,%d%c%c%c", baud, 22562306a36Sopenharmony_ci parity, bits, flow); 22662306a36Sopenharmony_ci else 22762306a36Sopenharmony_ci sprintf(console_string, " console=ttyS0,%d%c%c", baud, parity, 22862306a36Sopenharmony_ci bits); 22962306a36Sopenharmony_ci strlcat(arcs_cmdline, console_string, COMMAND_LINE_SIZE); 23062306a36Sopenharmony_ci#endif 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_civoid __init prom_init(void) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci ar7_init_cmdline(fw_arg0, (char **)fw_arg1); 23662306a36Sopenharmony_ci ar7_init_env((struct env_var *)fw_arg2); 23762306a36Sopenharmony_ci console_config(); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4))) 24162306a36Sopenharmony_cistatic inline unsigned int serial_in(int offset) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci return readl((void *)PORT(offset)); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic inline void serial_out(int offset, int value) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci writel(value, (void *)PORT(offset)); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_civoid prom_putchar(char c) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci while ((serial_in(UART_LSR) & UART_LSR_TEMT) == 0) 25462306a36Sopenharmony_ci ; 25562306a36Sopenharmony_ci serial_out(UART_TX, c); 25662306a36Sopenharmony_ci} 257