Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6] / arch / mips / pmc-sierra / msp71xx / msp_prom.c
1 /*
2  * BRIEF MODULE DESCRIPTION
3  *    PROM library initialisation code, assuming a version of
4  *    pmon is the boot code.
5  *
6  * Copyright 2000,2001 MontaVista Software Inc.
7  * Author: MontaVista Software, Inc.
8  *              ppopov@mvista.com or source@mvista.com
9  *
10  * This file was derived from Carsten Langgaard's
11  * arch/mips/mips-boards/xx files.
12  *
13  * Carsten Langgaard, carstenl@mips.com
14  * Copyright (C) 1999,2000 MIPS Technologies, Inc.  All rights reserved.
15  *
16  *  This program is free software; you can redistribute  it and/or modify it
17  *  under  the terms of  the GNU General  Public License as published by the
18  *  Free Software Foundation;  either version 2 of the  License, or (at your
19  *  option) any later version.
20  *
21  *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
22  *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
23  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
24  *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
25  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
27  *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28  *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
29  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  *  You should have received a copy of the  GNU General Public License along
33  *  with this program; if not, write  to the Free Software Foundation, Inc.,
34  *  675 Mass Ave, Cambridge, MA 02139, USA.
35  */
36
37 #include <linux/module.h>
38 #include <linux/kernel.h>
39 #include <linux/init.h>
40 #include <linux/string.h>
41 #include <linux/interrupt.h>
42 #include <linux/mm.h>
43 #ifdef CONFIG_CRAMFS
44 #include <linux/cramfs_fs.h>
45 #endif
46 #ifdef CONFIG_SQUASHFS
47 #include <linux/squashfs_fs.h>
48 #endif
49
50 #include <asm/addrspace.h>
51 #include <asm/bootinfo.h>
52 #include <asm-generic/sections.h>
53 #include <asm/page.h>
54
55 #include <msp_prom.h>
56 #include <msp_regs.h>
57
58 /* global PROM environment variables and pointers */
59 int prom_argc;
60 char **prom_argv, **prom_envp;
61 int *prom_vec;
62
63 /* debug flag */
64 int init_debug = 1;
65
66 /* memory blocks */
67 struct prom_pmemblock mdesc[PROM_MAX_PMEMBLOCKS];
68
69 /* default feature sets */
70 static char msp_default_features[] =
71 #if defined(CONFIG_PMC_MSP4200_EVAL) \
72  || defined(CONFIG_PMC_MSP4200_GW)
73         "ERER";
74 #elif defined(CONFIG_PMC_MSP7120_EVAL) \
75  || defined(CONFIG_PMC_MSP7120_GW)
76         "EMEMSP";
77 #elif defined(CONFIG_PMC_MSP7120_FPGA)
78         "EMEM";
79 #endif
80
81 /* conversion functions */
82 static inline unsigned char str2hexnum(unsigned char c)
83 {
84         if (c >= '0' && c <= '9')
85                 return c - '0';
86         if (c >= 'a' && c <= 'f')
87                 return c - 'a' + 10;
88         return 0; /* foo */
89 }
90
91 static inline int str2eaddr(unsigned char *ea, unsigned char *str)
92 {
93         int index = 0;
94         unsigned char num = 0;
95
96         while (*str != '\0') {
97                 if ((*str == '.') || (*str == ':')) {
98                         ea[index++] = num;
99                         num = 0;
100                         str++;
101                 } else {
102                         num = num << 4;
103                         num |= str2hexnum(*str++);
104                 }
105         }
106
107         if (index == 5) {
108                 ea[index++] = num;
109                 return 0;
110         } else
111                 return -1;
112 }
113 EXPORT_SYMBOL(str2eaddr);
114
115 static inline unsigned long str2hex(unsigned char *str)
116 {
117         int value = 0;
118
119         while (*str) {
120                 value = value << 4;
121                 value |= str2hexnum(*str++);
122         }
123
124         return value;
125 }
126
127 /* function to query the system information */
128 const char *get_system_type(void)
129 {
130 #if defined(CONFIG_PMC_MSP4200_EVAL)
131         return "PMC-Sierra MSP4200 Eval Board";
132 #elif defined(CONFIG_PMC_MSP4200_GW)
133         return "PMC-Sierra MSP4200 VoIP Gateway";
134 #elif defined(CONFIG_PMC_MSP7120_EVAL)
135         return "PMC-Sierra MSP7120 Eval Board";
136 #elif defined(CONFIG_PMC_MSP7120_GW)
137         return "PMC-Sierra MSP7120 Residential Gateway";
138 #elif defined(CONFIG_PMC_MSP7120_FPGA)
139         return "PMC-Sierra MSP7120 FPGA";
140 #else
141         #error "What is the type of *your* MSP?"
142 #endif
143 }
144
145 int get_ethernet_addr(char *ethaddr_name, char *ethernet_addr)
146 {
147         char *ethaddr_str;
148
149         ethaddr_str = prom_getenv(ethaddr_name);
150         if (!ethaddr_str) {
151                 printk(KERN_WARNING "%s not set in boot prom\n", ethaddr_name);
152                 return -1;
153         }
154
155         if (str2eaddr(ethernet_addr, ethaddr_str) == -1) {
156                 printk(KERN_WARNING "%s badly formatted-<%s>\n",
157                         ethaddr_name, ethaddr_str);
158                 return -1;
159         }
160
161         if (init_debug > 1) {
162                 int i;
163                 printk(KERN_DEBUG "get_ethernet_addr: for %s ", ethaddr_name);
164                 for (i = 0; i < 5; i++)
165                         printk(KERN_DEBUG "%02x:",
166                                 (unsigned char)*(ethernet_addr+i));
167                 printk(KERN_DEBUG "%02x\n", *(ethernet_addr+i));
168         }
169
170         return 0;
171 }
172 EXPORT_SYMBOL(get_ethernet_addr);
173
174 static char *get_features(void)
175 {
176         char *feature = prom_getenv(FEATURES);
177
178         if (feature == NULL) {
179                 /* default features based on MACHINE_TYPE */
180                 feature = msp_default_features;
181         }
182
183         return feature;
184 }
185
186 static char test_feature(char c)
187 {
188         char *feature = get_features();
189
190         while (*feature) {
191                 if (*feature++ == c)
192                         return *feature;
193                 feature++;
194         }
195
196         return FEATURE_NOEXIST;
197 }
198
199 unsigned long get_deviceid(void)
200 {
201         char *deviceid = prom_getenv(DEVICEID);
202
203         if (deviceid == NULL)
204                 return *DEV_ID_REG;
205         else
206                 return str2hex(deviceid);
207 }
208
209 char identify_pci(void)
210 {
211         return test_feature(PCI_KEY);
212 }
213 EXPORT_SYMBOL(identify_pci);
214
215 char identify_pcimux(void)
216 {
217         return test_feature(PCIMUX_KEY);
218 }
219
220 char identify_sec(void)
221 {
222         return test_feature(SEC_KEY);
223 }
224 EXPORT_SYMBOL(identify_sec);
225
226 char identify_spad(void)
227 {
228         return test_feature(SPAD_KEY);
229 }
230 EXPORT_SYMBOL(identify_spad);
231
232 char identify_tdm(void)
233 {
234         return test_feature(TDM_KEY);
235 }
236 EXPORT_SYMBOL(identify_tdm);
237
238 char identify_zsp(void)
239 {
240         return test_feature(ZSP_KEY);
241 }
242 EXPORT_SYMBOL(identify_zsp);
243
244 static char identify_enetfeature(char key, unsigned long interface_num)
245 {
246         char *feature = get_features();
247
248         while (*feature) {
249                 if (*feature++ == key && interface_num-- == 0)
250                         return *feature;
251                 feature++;
252         }
253
254         return FEATURE_NOEXIST;
255 }
256
257 char identify_enet(unsigned long interface_num)
258 {
259         return identify_enetfeature(ENET_KEY, interface_num);
260 }
261 EXPORT_SYMBOL(identify_enet);
262
263 char identify_enetTxD(unsigned long interface_num)
264 {
265         return identify_enetfeature(ENETTXD_KEY, interface_num);
266 }
267 EXPORT_SYMBOL(identify_enetTxD);
268
269 unsigned long identify_family(void)
270 {
271         unsigned long deviceid;
272
273         deviceid = get_deviceid();
274
275         return deviceid & CPU_DEVID_FAMILY;
276 }
277 EXPORT_SYMBOL(identify_family);
278
279 unsigned long identify_revision(void)
280 {
281         unsigned long deviceid;
282
283         deviceid = get_deviceid();
284
285         return deviceid & CPU_DEVID_REVISION;
286 }
287 EXPORT_SYMBOL(identify_revision);
288
289 /* PROM environment functions */
290 char *prom_getenv(char *env_name)
291 {
292         /*
293          * Return a pointer to the given environment variable.  prom_envp
294          * points to a null terminated array of pointers to variables.
295          * Environment variables are stored in the form of "memsize=64"
296          */
297
298         char **var = prom_envp;
299         int i = strlen(env_name);
300
301         while (*var) {
302                 if (strncmp(env_name, *var, i) == 0) {
303                         return (*var + strlen(env_name) + 1);
304                 }
305                 var++;
306         }
307
308         return NULL;
309 }
310
311 /* PROM commandline functions */
312 char *prom_getcmdline(void)
313 {
314         return &(arcs_cmdline[0]);
315 }
316 EXPORT_SYMBOL(prom_getcmdline);
317
318 void  __init prom_init_cmdline(void)
319 {
320         char *cp;
321         int actr;
322
323         actr = 1; /* Always ignore argv[0] */
324
325         cp = &(arcs_cmdline[0]);
326         while (actr < prom_argc) {
327                 strcpy(cp, prom_argv[actr]);
328                 cp += strlen(prom_argv[actr]);
329                 *cp++ = ' ';
330                 actr++;
331         }
332         if (cp != &(arcs_cmdline[0])) /* get rid of trailing space */
333                 --cp;
334         *cp = '\0';
335 }
336
337 /* memory allocation functions */
338 static int __init prom_memtype_classify(unsigned int type)
339 {
340         switch (type) {
341         case yamon_free:
342                 return BOOT_MEM_RAM;
343         case yamon_prom:
344                 return BOOT_MEM_ROM_DATA;
345         default:
346                 return BOOT_MEM_RESERVED;
347         }
348 }
349
350 void __init prom_meminit(void)
351 {
352         struct prom_pmemblock *p;
353
354         p = prom_getmdesc();
355
356         while (p->size) {
357                 long type;
358                 unsigned long base, size;
359
360                 type = prom_memtype_classify(p->type);
361                 base = p->base;
362                 size = p->size;
363
364                 add_memory_region(base, size, type);
365                 p++;
366         }
367 }
368
369 void __init prom_free_prom_memory(void)
370 {
371         int     argc;
372         char    **argv;
373         char    **envp;
374         char    *ptr;
375         int     len = 0;
376         int     i;
377         unsigned long addr;
378
379         /*
380          * preserve environment variables and command line from pmon/bbload
381          * first preserve the command line
382          */
383         for (argc = 0; argc < prom_argc; argc++) {
384                 len += sizeof(char *);                  /* length of pointer */
385                 len += strlen(prom_argv[argc]) + 1;     /* length of string */
386         }
387         len += sizeof(char *);          /* plus length of null pointer */
388
389         argv = kmalloc(len, GFP_KERNEL);
390         ptr = (char *) &argv[prom_argc + 1];    /* strings follow array */
391
392         for (argc = 0; argc < prom_argc; argc++) {
393                 argv[argc] = ptr;
394                 strcpy(ptr, prom_argv[argc]);
395                 ptr += strlen(prom_argv[argc]) + 1;
396         }
397         argv[prom_argc] = NULL;         /* end array with null pointer */
398         prom_argv = argv;
399
400         /* next preserve the environment variables */
401         len = 0;
402         i = 0;
403         for (envp = prom_envp; *envp != NULL; envp++) {
404                 i++;            /* count number of environment variables */
405                 len += sizeof(char *);          /* length of pointer */
406                 len += strlen(*envp) + 1;       /* length of string */
407         }
408         len += sizeof(char *);          /* plus length of null pointer */
409
410         envp = kmalloc(len, GFP_KERNEL);
411         ptr = (char *) &envp[i+1];
412
413         for (argc = 0; argc < i; argc++) {
414                 envp[argc] = ptr;
415                 strcpy(ptr, prom_envp[argc]);
416                 ptr += strlen(prom_envp[argc]) + 1;
417         }
418         envp[i] = NULL;                 /* end array with null pointer */
419         prom_envp = envp;
420
421         for (i = 0; i < boot_mem_map.nr_map; i++) {
422                 if (boot_mem_map.map[i].type != BOOT_MEM_ROM_DATA)
423                         continue;
424
425                 addr = boot_mem_map.map[i].addr;
426                 free_init_pages("prom memory",
427                                 addr, addr + boot_mem_map.map[i].size);
428         }
429 }
430
431 struct prom_pmemblock *__init prom_getmdesc(void)
432 {
433         static char     memsz_env[] __initdata = "memsize";
434         static char     heaptop_env[] __initdata = "heaptop";
435         char            *str;
436         unsigned int    memsize;
437         unsigned int    heaptop;
438 #ifdef CONFIG_MTD_PMC_MSP_RAMROOT
439         void            *ramroot_start;
440         unsigned long   ramroot_size;
441 #endif
442         int i;
443
444         str = prom_getenv(memsz_env);
445         if (!str) {
446                 ppfinit("memsize not set in boot prom, "
447                         "set to default (32Mb)\n");
448                 memsize = 0x02000000;
449         } else {
450                 memsize = simple_strtol(str, NULL, 0);
451
452                 if (memsize == 0) {
453                         /* if memsize is a bad size, use reasonable default */
454                         memsize = 0x02000000;
455                 }
456
457                 /* convert to physical address (removing caching bits, etc) */
458                 memsize = CPHYSADDR(memsize);
459         }
460
461         str = prom_getenv(heaptop_env);
462         if (!str) {
463                 heaptop = CPHYSADDR((u32)&_text);
464                 ppfinit("heaptop not set in boot prom, "
465                         "set to default 0x%08x\n", heaptop);
466         } else {
467                 heaptop = simple_strtol(str, NULL, 16);
468                 if (heaptop == 0) {
469                         /* heaptop conversion bad, might have 0xValue */
470                         heaptop = simple_strtol(str, NULL, 0);
471
472                         if (heaptop == 0) {
473                                 /* heaptop still bad, use reasonable default */
474                                 heaptop = CPHYSADDR((u32)&_text);
475                         }
476                 }
477
478                 /* convert to physical address (removing caching bits, etc) */
479                 heaptop = CPHYSADDR((u32)heaptop);
480         }
481
482         /* the base region */
483         i = 0;
484         mdesc[i].type = BOOT_MEM_RESERVED;
485         mdesc[i].base = 0x00000000;
486         mdesc[i].size = PAGE_ALIGN(0x300 + 0x80);
487                 /* jtag interrupt vector + sizeof vector */
488
489         /* PMON data */
490         if (heaptop > mdesc[i].base + mdesc[i].size) {
491                 i++;                    /* 1 */
492                 mdesc[i].type = BOOT_MEM_ROM_DATA;
493                 mdesc[i].base = mdesc[i-1].base + mdesc[i-1].size;
494                 mdesc[i].size = heaptop - mdesc[i].base;
495         }
496
497         /* end of PMON data to start of kernel -- probably zero .. */
498         if (heaptop != CPHYSADDR((u32)_text)) {
499                 i++;    /* 2 */
500                 mdesc[i].type = BOOT_MEM_RAM;
501                 mdesc[i].base = heaptop;
502                 mdesc[i].size = CPHYSADDR((u32)_text) - mdesc[i].base;
503         }
504
505         /*  kernel proper */
506         i++;                    /* 3 */
507         mdesc[i].type = BOOT_MEM_RESERVED;
508         mdesc[i].base = CPHYSADDR((u32)_text);
509 #ifdef CONFIG_MTD_PMC_MSP_RAMROOT
510         if (get_ramroot(&ramroot_start, &ramroot_size)) {
511                 /*
512                  * Rootfs in RAM -- follows kernel
513                  * Combine rootfs image with kernel block so a
514                  * page (4k) isn't wasted between memory blocks
515                  */
516                 mdesc[i].size = CPHYSADDR(PAGE_ALIGN(
517                         (u32)ramroot_start + ramroot_size)) - mdesc[i].base;
518         } else
519 #endif
520                 mdesc[i].size = CPHYSADDR(PAGE_ALIGN(
521                         (u32)_end)) - mdesc[i].base;
522
523         /* Remainder of RAM -- under memsize */
524         i++;                    /* 5 */
525         mdesc[i].type = yamon_free;
526         mdesc[i].base = mdesc[i-1].base + mdesc[i-1].size;
527         mdesc[i].size = memsize - mdesc[i].base;
528
529         return &mdesc[0];
530 }
531
532 /* rootfs functions */
533 #ifdef CONFIG_MTD_PMC_MSP_RAMROOT
534 bool get_ramroot(void **start, unsigned long *size)
535 {
536         extern char _end[];
537
538         /* Check for start following the end of the kernel */
539         void *check_start = (void *)_end;
540
541         /* Check for supported rootfs types */
542 #ifdef CONFIG_CRAMFS
543         if (*(__u32 *)check_start == CRAMFS_MAGIC) {
544                 /* Get CRAMFS size */
545                 *start = check_start;
546                 *size = PAGE_ALIGN(((struct cramfs_super *)
547                                    check_start)->size);
548
549                 return true;
550         }
551 #endif
552 #ifdef CONFIG_SQUASHFS
553         if (*((unsigned int *)check_start) == SQUASHFS_MAGIC) {
554                 /* Get SQUASHFS size */
555                 *start = check_start;
556                 *size = PAGE_ALIGN(((struct squashfs_super_block *)
557                                    check_start)->bytes_used);
558
559                 return true;
560         }
561 #endif
562
563         return false;
564 }
565 EXPORT_SYMBOL(get_ramroot);
566 #endif