Changes for kernel and Busybox
[tomato.git] / release / src / linux / linux / arch / mips / bcm947xx / setup.c
blob38572177a5bd20f56d187e3ecce603a4df50521d
1 /*
2 * Generic setup routines for Broadcom MIPS boards
4 * Copyright (C) 2005 Felix Fietkau <nbd@openwrt.org>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
11 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
12 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
14 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
15 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
16 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
17 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
18 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
19 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
20 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 675 Mass Ave, Cambridge, MA 02139, USA.
27 * Copyright 2005, Broadcom Corporation
28 * All Rights Reserved.
30 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
31 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
32 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
33 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
37 #include <linux/config.h>
38 #include <linux/init.h>
39 #include <linux/kernel.h>
40 #include <linux/module.h>
41 #include <linux/serialP.h>
42 #include <linux/ide.h>
43 #include <asm/bootinfo.h>
44 #include <asm/cpu.h>
45 #include <asm/time.h>
46 #include <asm/reboot.h>
48 #include <typedefs.h>
49 #include <osl.h>
50 #include <sbutils.h>
51 #include <bcmutils.h>
52 #include <bcmnvram.h>
53 #include <bcmdevs.h>
54 #include <sbhndmips.h>
55 #include <hndmips.h>
56 #include <trxhdr.h>
57 #include "bcm947xx.h"
58 #include <linux/mtd/mtd.h>
59 #ifdef CONFIG_MTD_PARTITIONS
60 #include <linux/mtd/partitions.h>
61 #endif
62 #include <linux/romfs_fs.h>
63 #include <linux/cramfs_fs.h>
64 #include <linux/minix_fs.h>
65 #include <linux/ext2_fs.h>
67 /* Global SB handle */
68 sb_t *bcm947xx_sbh = NULL;
69 spinlock_t bcm947xx_sbh_lock = SPIN_LOCK_UNLOCKED;
70 EXPORT_SYMBOL(bcm947xx_sbh);
71 EXPORT_SYMBOL(bcm947xx_sbh_lock);
73 /* CPU freq Tomato RAF features */
74 int bcm947xx_cpu_clk;
75 EXPORT_SYMBOL(bcm947xx_cpu_clk);
77 /* Convenience */
78 #define sbh bcm947xx_sbh
79 #define sbh_lock bcm947xx_sbh_lock
81 extern void bcm947xx_time_init(void);
82 extern void bcm947xx_timer_setup(struct irqaction *irq);
84 #ifdef CONFIG_REMOTE_DEBUG
85 extern void set_debug_traps(void);
86 extern void rs_kgdb_hook(struct serial_state *);
87 extern void breakpoint(void);
88 #endif
90 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
91 extern struct ide_ops std_ide_ops;
92 #endif
94 /* Kernel command line */
95 char arcs_cmdline[CL_SIZE] __initdata = CONFIG_CMDLINE;
96 extern void sb_serial_init(sb_t *sbh, void (*add)(void *regs, uint irq, uint baud_base, uint reg_shift));
98 void
99 bcm947xx_machine_restart(char *command)
101 printk("Please stand by while rebooting the system...\n");
103 if (sb_chip(sbh) == BCM4785_CHIP_ID)
104 MTC0(C0_BROADCOM, 4, (1 << 22));
106 /* Set the watchdog timer to reset immediately */
107 __cli();
108 sb_watchdog(sbh, 1);
110 if (sb_chip(sbh) == BCM4785_CHIP_ID) {
111 __asm__ __volatile__(
112 ".set\tmips3\n\t"
113 "sync\n\t"
114 "wait\n\t"
115 ".set\tmips0");
118 while (1);
121 void
122 bcm947xx_machine_halt(void)
124 printk("System halted\n");
126 /* Disable interrupts and watchdog and spin forever */
127 __cli();
128 sb_watchdog(sbh, 0);
129 while (1);
132 #ifdef CONFIG_SERIAL
134 static int ser_line = 0;
136 typedef struct {
137 void *regs;
138 uint irq;
139 uint baud_base;
140 uint reg_shift;
141 } serial_port;
143 static serial_port ports[4];
144 static int num_ports = 0;
146 static void
147 serial_add(void *regs, uint irq, uint baud_base, uint reg_shift)
149 ports[num_ports].regs = regs;
150 ports[num_ports].irq = irq;
151 ports[num_ports].baud_base = baud_base;
152 ports[num_ports].reg_shift = reg_shift;
153 num_ports++;
156 static void
157 do_serial_add(serial_port *port)
159 void *regs;
160 uint irq;
161 uint baud_base;
162 uint reg_shift;
163 struct serial_struct s;
165 regs = port->regs;
166 irq = port->irq;
167 baud_base = port->baud_base;
168 reg_shift = port->reg_shift;
170 memset(&s, 0, sizeof(s));
172 s.line = ser_line++;
173 s.iomem_base = regs;
174 s.irq = irq + 2;
175 s.baud_base = baud_base / 16;
176 s.flags = ASYNC_BOOT_AUTOCONF;
177 s.io_type = SERIAL_IO_MEM;
178 s.iomem_reg_shift = reg_shift;
180 if (early_serial_setup(&s) != 0) {
181 printk(KERN_ERR "Serial setup failed!\n");
185 #endif /* CONFIG_SERIAL */
187 void __init
188 brcm_setup(void)
190 int i;
191 char *value;
193 /* Get global SB handle */
194 sbh = sb_kattach(SB_OSH);
196 /* Initialize clocks and interrupts */
197 sb_mips_init(sbh, SBMIPS_VIRTIRQ_BASE);
199 if (BCM330X(current_cpu_data.processor_id) &&
200 (read_c0_diag() & BRCM_PFC_AVAIL)) {
202 * Now that the sbh is inited set the proper PFC value
204 printk("Setting the PFC to its default value\n");
205 enable_pfc(PFC_AUTO);
209 value = nvram_get("kernel_args");
210 #ifdef CONFIG_SERIAL
211 sb_serial_init(sbh, serial_add);
213 /* reverse serial ports if nvram variable contains console=ttyS1 */
214 /* Initialize UARTs */
215 if (value && strlen(value) && strstr(value, "console=ttyS1")!=NULL) {
216 for (i = num_ports; i; i--)
217 do_serial_add(&ports[i - 1]);
218 } else {
219 for (i = 0; i < num_ports; i++)
220 do_serial_add(&ports[i]);
222 #endif
224 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
225 ide_ops = &std_ide_ops;
226 #endif
228 /* Override default command line arguments */
229 if (value && strlen(value) && strncmp(value, "empty", 5))
230 strncpy(arcs_cmdline, value, sizeof(arcs_cmdline));
233 /* Generic setup */
234 _machine_restart = bcm947xx_machine_restart;
235 _machine_halt = bcm947xx_machine_halt;
236 _machine_power_off = bcm947xx_machine_halt;
238 board_time_init = bcm947xx_time_init;
239 board_timer_setup = bcm947xx_timer_setup;
242 const char *
243 get_system_type(void)
245 static char s[40];
247 if (bcm947xx_sbh) {
248 sprintf(s, "Broadcom BCM%X chip rev %d pkg %d", sb_chip(bcm947xx_sbh),
249 sb_chiprev(bcm947xx_sbh), sb_chippkg(bcm947xx_sbh));
250 return s;
252 else
253 return "Broadcom BCM947XX";
256 void __init
257 bus_error_init(void)
262 #ifdef CONFIG_MTD_PARTITIONS
264 enum {
265 RT_UNKNOWN,
266 RT_DIR320 // D-Link DIR-320
269 static int get_router(void)
271 uint boardnum = bcm_strtoul(nvram_safe_get("boardnum"), NULL, 0);
272 uint boardrev = bcm_strtoul(nvram_safe_get("boardrev"), NULL, 0);
273 uint boardtype = bcm_strtoul(nvram_safe_get("boardtype"), NULL, 0);
275 #ifdef DIR320_BOARD
276 if (boardnum == 0 && boardtype == 0x48E && boardrev == 0x35) {
277 return RT_DIR320;
279 #endif
281 return RT_UNKNOWN;
284 #ifdef DIR320_BOARD
285 static size_t get_erasesize(struct mtd_info *mtd, size_t offset, size_t size)
287 int i;
288 struct mtd_erase_region_info *regions;
289 size_t erasesize = 0;
291 if (mtd->numeraseregions > 1) {
292 regions = mtd->eraseregions;
294 // Find the first erase regions which is part of this partition
295 for (i = 0; i < mtd->numeraseregions && offset >= regions[i].offset; i++);
297 for (i--; i < mtd->numeraseregions && offset + size > regions[i].offset; i++) {
298 if (erasesize < regions[i].erasesize)
299 erasesize = regions[i].erasesize;
302 else {
303 erasesize = mtd->erasesize;
306 return erasesize;
308 #endif // DIR320_BOARD
311 new layout -- zzz 04/2006
313 +--------------+
314 | boot |
315 +---+----------+ < search for HDR0
316 | | |
317 | | (kernel) |
318 | l | |
319 | i +----------+ < + trx->offset[1]
320 | n | |
321 | u | rootfs |
322 | x | |
323 + +----------+ < + trx->len
324 | | jffs2 |
325 +---+----------+ < size - NVRAM_SPACE - board_data_size()
326 | board_data |
327 +--------------+ < size - NVRAM_SPACE
328 | nvram |
329 +--------------+ < size
332 static struct mtd_partition bcm947xx_parts[] = {
333 { name: "pmon", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, },
334 { name: "linux", offset: 0, size: 0, },
335 { name: "rootfs", offset: 0, size: 0, mask_flags: MTD_WRITEABLE, },
336 { name: "jffs2", offset: 0, size: 0, },
337 { name: "nvram", offset: 0, size: 0, },
338 { name: "board_data", offset: 0, size: 0, },
339 { name: NULL, },
342 #define PART_BOOT 0
343 #define PART_LINUX 1
344 #define PART_ROOTFS 2
345 #define PART_JFFS2 3
346 #define PART_NVRAM 4
347 #define PART_BOARD 5
349 struct mtd_partition * __init
350 init_mtd_partitions(struct mtd_info *mtd, size_t size)
352 int router;
353 struct trx_header *trx;
354 unsigned char buf[512];
355 size_t off, trxoff, boardoff;
356 size_t len;
357 size_t trxsize;
359 /* Find and size nvram */
360 bcm947xx_parts[PART_NVRAM].size = ROUNDUP(NVRAM_SPACE, mtd->erasesize);
361 bcm947xx_parts[PART_NVRAM].offset = size - bcm947xx_parts[PART_NVRAM].size;
363 /* Size board_data */
364 boardoff = bcm947xx_parts[PART_NVRAM].offset;
365 router = get_router();
366 switch (router) {
367 #ifdef DIR320_BOARD
368 case RT_DIR320:
369 if (get_erasesize(mtd, bcm947xx_parts[PART_NVRAM].offset, bcm947xx_parts[PART_NVRAM].size) == 0x2000) {
370 bcm947xx_parts[PART_NVRAM].size = ROUNDUP(NVRAM_SPACE, 0x2000);
371 bcm947xx_parts[PART_NVRAM].offset = size - bcm947xx_parts[PART_NVRAM].size;
372 bcm947xx_parts[PART_BOARD].size = 0x2000; // 8 KB
373 bcm947xx_parts[PART_BOARD].offset = bcm947xx_parts[PART_NVRAM].offset - bcm947xx_parts[PART_BOARD].size;
375 else bcm947xx_parts[PART_BOARD].name = NULL;
376 break;
377 #endif
378 default:
379 bcm947xx_parts[PART_BOARD].name = NULL;
380 break;
383 trxsize = 0;
384 trx = (struct trx_header *) buf;
385 for (off = 0; off < size; off += mtd->erasesize) {
387 * Read block 0 to test for rootfs
389 if ((MTD_READ(mtd, off, sizeof(buf), &len, buf)) || (len != sizeof(buf))) continue;
391 /* Try looking at TRX header for rootfs offset */
392 if (le32_to_cpu(trx->magic) == TRX_MAGIC) {
393 /* Size pmon */
394 bcm947xx_parts[PART_BOOT].size = off;
396 /* Size linux (kernel and rootfs) */
397 bcm947xx_parts[PART_LINUX].offset = off;
398 bcm947xx_parts[PART_LINUX].size = boardoff - off;
400 trxsize = ROUNDUP(le32_to_cpu(trx->len), mtd->erasesize); // kernel + rootfs
402 /* Find and size rootfs */
403 trxoff = (le32_to_cpu(trx->offsets[2]) > off) ? trx->offsets[2] : trx->offsets[1];
404 bcm947xx_parts[PART_ROOTFS].offset = trxoff + off;
405 bcm947xx_parts[PART_ROOTFS].size = trxsize - trxoff;
407 /* Find and size jffs2 */
408 bcm947xx_parts[PART_JFFS2].offset = off + trxsize;
409 if (boardoff > bcm947xx_parts[PART_JFFS2].offset)
410 bcm947xx_parts[PART_JFFS2].size = boardoff - bcm947xx_parts[PART_JFFS2].offset;
412 break;
416 if (trxsize == 0) {
417 // uh, now what...
418 printk(KERN_NOTICE "%s: Unable to find a valid linux partition\n", mtd->name);
421 #if 0
422 int i;
423 for (i = 0; bcm947xx_parts[i].name; ++i) {
424 printk(KERN_NOTICE "%8x %8x (%8x) %s\n",
425 bcm947xx_parts[i].offset,
426 (bcm947xx_parts[i].offset + bcm947xx_parts[i].size) - 1,
427 bcm947xx_parts[i].size,
428 bcm947xx_parts[i].name);
430 #endif
432 return bcm947xx_parts;
435 #if 0
436 struct mtd_partition * __init
437 init_mtd_partitions(struct mtd_info *mtd, size_t size)
439 struct minix_super_block *minixsb;
440 struct ext2_super_block *ext2sb;
441 struct romfs_super_block *romfsb;
442 struct squashfs_super_block *squashfsb;
443 struct cramfs_super *cramfsb;
444 struct trx_header *trx;
445 unsigned char buf[512];
446 int off, trx_len = 0;
447 size_t len;
449 minixsb = (struct minix_super_block *) buf;
450 ext2sb = (struct ext2_super_block *) buf;
451 romfsb = (struct romfs_super_block *) buf;
452 squashfsb = (struct squashfs_super_block *) buf;
453 cramfsb = (struct cramfs_super *) buf;
454 trx = (struct trx_header *) buf;
456 /* Look at every 64 KB boundary */
457 for (off = 0; off < size; off += (64 * 1024)) {
458 memset(buf, 0xe5, sizeof(buf));
461 * Read block 0 to test for romfs and cramfs superblock
463 if (MTD_READ(mtd, off, sizeof(buf), &len, buf) ||
464 len != sizeof(buf))
465 continue;
467 /* Try looking at TRX header for rootfs offset */
468 if (le32_to_cpu(trx->magic) == TRX_MAGIC) {
469 bcm947xx_parts[PART_LINUX].offset = off;
470 trx_len = trx->len;
471 if (le32_to_cpu(trx->offsets[2]) > off)
472 off = le32_to_cpu(trx->offsets[2]);
473 else if (le32_to_cpu(trx->offsets[1]) > off)
474 off = le32_to_cpu(trx->offsets[1]);
475 continue;
478 /* romfs is at block zero too */
479 if (romfsb->word0 == ROMSB_WORD0 &&
480 romfsb->word1 == ROMSB_WORD1) {
481 printk(KERN_NOTICE
482 "%s: romfs filesystem found at block %d\n",
483 mtd->name, off / BLOCK_SIZE);
484 goto done;
487 /* squashfs is at block zero too */
488 if (squashfsb->s_magic == SQUASHFS_MAGIC) {
489 printk(KERN_NOTICE
490 "%s: squashfs filesystem found at block %d\n",
491 mtd->name, off / BLOCK_SIZE);
492 goto done;
495 /* so is cramfs */
496 if (cramfsb->magic == CRAMFS_MAGIC) {
497 printk(KERN_NOTICE
498 "%s: cramfs filesystem found at block %d\n",
499 mtd->name, off / BLOCK_SIZE);
500 goto done;
504 * Read block 1 to test for minix and ext2 superblock
506 if (MTD_READ(mtd, off + BLOCK_SIZE, sizeof(buf), &len, buf) ||
507 len != sizeof(buf))
508 continue;
510 /* Try minix */
511 if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
512 minixsb->s_magic == MINIX_SUPER_MAGIC2) {
513 printk(KERN_NOTICE
514 "%s: Minix filesystem found at block %d\n",
515 mtd->name, off / BLOCK_SIZE);
516 goto done;
519 /* Try ext2 */
520 if (ext2sb->s_magic == cpu_to_le16(EXT2_SUPER_MAGIC)) {
521 printk(KERN_NOTICE
522 "%s: ext2 filesystem found at block %d\n",
523 mtd->name, off / BLOCK_SIZE);
524 goto done;
528 // uh, now what...
529 printk(KERN_NOTICE "%s: Unable to find a valid linux partition\n", mtd->name);
531 done:
532 /* Find and size nvram */
533 bcm947xx_parts[PART_NVRAM].offset = size - ROUNDUP(NVRAM_SPACE, mtd->erasesize);
534 bcm947xx_parts[PART_NVRAM].size = size - bcm947xx_parts[PART_NVRAM].offset;
536 /* Find and size rootfs */
537 if (off < size) {
538 bcm947xx_parts[PART_ROOTFS].offset = off;
539 bcm947xx_parts[PART_ROOTFS].size = bcm947xx_parts[PART_NVRAM].offset - bcm947xx_parts[PART_ROOTFS].offset;
542 /* Size linux (kernel and rootfs) */
543 bcm947xx_parts[PART_LINUX].size = bcm947xx_parts[PART_NVRAM].offset - bcm947xx_parts[PART_LINUX].offset;
545 /* Size pmon */
546 bcm947xx_parts[PART_BOOT].size = bcm947xx_parts[PART_LINUX].offset - bcm947xx_parts[PART_BOOT].offset;
548 /* Find and size jffs2 -- nvram reserved + pmon */
549 bcm947xx_parts[PART_JFFS2].size = (128*1024 - bcm947xx_parts[PART_NVRAM].size) +
550 (256*1024 - bcm947xx_parts[PART_BOOT].size);
551 /* ... add unused space above 4MB */
552 if (size > 0x400000) {
553 if (trx_len <= 0x3a0000) // Small firmware - fixed amount
554 bcm947xx_parts[PART_JFFS2].size += size - 0x400000;
555 else {
556 bcm947xx_parts[PART_JFFS2].size += size - (trx_len + 128*1024 + 256*1024);
557 bcm947xx_parts[PART_JFFS2].size &= (~0xFFFFUL); // Round down 64K
560 bcm947xx_parts[PART_JFFS2].offset = bcm947xx_parts[PART_NVRAM].offset - bcm947xx_parts[PART_JFFS2].size;
561 if (bcm947xx_parts[PART_JFFS2].size <= 0) {
562 bcm947xx_parts[PART_JFFS2].name = NULL;
563 bcm947xx_parts[PART_JFFS2].size = 0;
565 else {
566 bcm947xx_parts[PART_ROOTFS].size = bcm947xx_parts[PART_JFFS2].offset - bcm947xx_parts[PART_ROOTFS].offset;
569 return bcm947xx_parts;
571 #endif // 0
573 EXPORT_SYMBOL(init_mtd_partitions);
575 #endif