1 /* 2 * Carsten Langgaard, carstenl@mips.com 3 * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. 4 * 5 * This program is free software; you can distribute it and/or modify it 6 * under the terms of the GNU General Public License (Version 2) as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * for more details. 13 * 14 * You should have received a copy of the GNU General Public License along 15 * with this program; if not, write to the Free Software Foundation, Inc., 16 * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 17 * 18 * Putting things on the screen/serial line using YAMONs facilities. 19 */ 20 #include <linux/init.h> 21 #include <linux/kernel.h> 22 #include <linux/serial_reg.h> 23 #include <linux/spinlock.h> 24 #include <linux/export.h> 25 #include <linux/string.h> 26 #include <linux/io.h> 27 #include <asm/bootinfo.h> 28 #include <asm/setup.h> 29 30 #include <asm/mach-ar7/ar7.h> 31 #include <asm/mach-ar7/prom.h> 32 33 #define MAX_ENTRY 80 34 35 struct env_var { 36 char *name; 37 char *value; 38 }; 39 40 static struct env_var adam2_env[MAX_ENTRY]; 41 42 char *prom_getenv(const char *name) 43 { 44 int i; 45 46 for (i = 0; (i < MAX_ENTRY) && adam2_env[i].name; i++) 47 if (!strcmp(name, adam2_env[i].name)) 48 return adam2_env[i].value; 49 50 return NULL; 51 } 52 EXPORT_SYMBOL(prom_getenv); 53 54 static void __init ar7_init_cmdline(int argc, char *argv[]) 55 { 56 int i; 57 58 for (i = 1; i < argc; i++) { 59 strlcat(arcs_cmdline, argv[i], COMMAND_LINE_SIZE); 60 if (i < (argc - 1)) 61 strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); 62 } 63 } 64 65 struct psbl_rec { 66 u32 psbl_size; 67 u32 env_base; 68 u32 env_size; 69 u32 ffs_base; 70 u32 ffs_size; 71 }; 72 73 static const char psp_env_version[] __initconst = "TIENV0.8"; 74 75 struct psp_env_chunk { 76 u8 num; 77 u8 ctrl; 78 u16 csum; 79 u8 len; 80 char data[11]; 81 } __packed; 82 83 struct psp_var_map_entry { 84 u8 num; 85 char *value; 86 }; 87 88 static const struct psp_var_map_entry psp_var_map[] = { 89 { 1, "cpufrequency" }, 90 { 2, "memsize" }, 91 { 3, "flashsize" }, 92 { 4, "modetty0" }, 93 { 5, "modetty1" }, 94 { 8, "maca" }, 95 { 9, "macb" }, 96 { 28, "sysfrequency" }, 97 { 38, "mipsfrequency" }, 98 }; 99 100 /* 101 102 Well-known variable (num is looked up in table above for matching variable name) 103 Example: cpufrequency=211968000 104 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 105 | 01 |CTRL|CHECKSUM | 01 | _2 | _1 | _1 | _9 | _6 | _8 | _0 | _0 | _0 | \0 | FF 106 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 107 108 Name=Value pair in a single chunk 109 Example: NAME=VALUE 110 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 111 | 00 |CTRL|CHECKSUM | 01 | _N | _A | _M | _E | _0 | _V | _A | _L | _U | _E | \0 112 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 113 114 Name=Value pair in 2 chunks (len is the number of chunks) 115 Example: bootloaderVersion=1.3.7.15 116 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 117 | 00 |CTRL|CHECKSUM | 02 | _b | _o | _o | _t | _l | _o | _a | _d | _e | _r | _V 118 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 119 | _e | _r | _s | _i | _o | _n | \0 | _1 | _. | _3 | _. | _7 | _. | _1 | _5 | \0 120 +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- 121 122 Data is padded with 0xFF 123 124 */ 125 126 #define PSP_ENV_SIZE 4096 127 128 static char psp_env_data[PSP_ENV_SIZE] = { 0, }; 129 130 static char * __init lookup_psp_var_map(u8 num) 131 { 132 int i; 133 134 for (i = 0; i < ARRAY_SIZE(psp_var_map); i++) 135 if (psp_var_map[i].num == num) 136 return psp_var_map[i].value; 137 138 return NULL; 139 } 140 141 static void __init add_adam2_var(char *name, char *value) 142 { 143 int i; 144 145 for (i = 0; i < MAX_ENTRY; i++) { 146 if (!adam2_env[i].name) { 147 adam2_env[i].name = name; 148 adam2_env[i].value = value; 149 return; 150 } else if (!strcmp(adam2_env[i].name, name)) { 151 adam2_env[i].value = value; 152 return; 153 } 154 } 155 } 156 157 static int __init parse_psp_env(void *psp_env_base) 158 { 159 int i, n; 160 char *name, *value; 161 struct psp_env_chunk *chunks = (struct psp_env_chunk *)psp_env_data; 162 163 memcpy_fromio(chunks, psp_env_base, PSP_ENV_SIZE); 164 165 i = 1; 166 n = PSP_ENV_SIZE / sizeof(struct psp_env_chunk); 167 while (i < n) { 168 if ((chunks[i].num == 0xff) || ((i + chunks[i].len) > n)) 169 break; 170 value = chunks[i].data; 171 if (chunks[i].num) { 172 name = lookup_psp_var_map(chunks[i].num); 173 } else { 174 name = value; 175 value += strlen(name) + 1; 176 } 177 if (name) 178 add_adam2_var(name, value); 179 i += chunks[i].len; 180 } 181 return 0; 182 } 183 184 static void __init ar7_init_env(struct env_var *env) 185 { 186 int i; 187 struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300)); 188 void *psp_env = (void *)KSEG1ADDR(psbl->env_base); 189 190 if (strcmp(psp_env, psp_env_version) == 0) { 191 parse_psp_env(psp_env); 192 } else { 193 for (i = 0; i < MAX_ENTRY; i++, env++) 194 if (env->name) 195 add_adam2_var(env->name, env->value); 196 } 197 } 198 199 static void __init console_config(void) 200 { 201 #ifdef CONFIG_SERIAL_8250_CONSOLE 202 char console_string[40]; 203 int baud = 0; 204 char parity = '\0', bits = '\0', flow = '\0'; 205 char *s, *p; 206 207 if (strstr(arcs_cmdline, "console=")) 208 return; 209 210 s = prom_getenv("modetty0"); 211 if (s) { 212 baud = simple_strtoul(s, &p, 10); 213 s = p; 214 if (*s == ',') 215 s++; 216 if (*s) 217 parity = *s++; 218 if (*s == ',') 219 s++; 220 if (*s) 221 bits = *s++; 222 if (*s == ',') 223 s++; 224 if (*s == 'h') 225 flow = 'r'; 226 } 227 228 if (baud == 0) 229 baud = 38400; 230 if (parity != 'n' && parity != 'o' && parity != 'e') 231 parity = 'n'; 232 if (bits != '7' && bits != '8') 233 bits = '8'; 234 235 if (flow == 'r') 236 sprintf(console_string, " console=ttyS0,%d%c%c%c", baud, 237 parity, bits, flow); 238 else 239 sprintf(console_string, " console=ttyS0,%d%c%c", baud, parity, 240 bits); 241 strlcat(arcs_cmdline, console_string, COMMAND_LINE_SIZE); 242 #endif 243 } 244 245 void __init prom_init(void) 246 { 247 ar7_init_cmdline(fw_arg0, (char **)fw_arg1); 248 ar7_init_env((struct env_var *)fw_arg2); 249 console_config(); 250 } 251 252 #define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4))) 253 static inline unsigned int serial_in(int offset) 254 { 255 return readl((void *)PORT(offset)); 256 } 257 258 static inline void serial_out(int offset, int value) 259 { 260 writel(value, (void *)PORT(offset)); 261 } 262 263 void prom_putchar(char c) 264 { 265 while ((serial_in(UART_LSR) & UART_LSR_TEMT) == 0) 266 ; 267 serial_out(UART_TX, c); 268 } 269