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>
46 #include <asm/reboot.h>
54 #include <sbhndmips.h>
58 #include <linux/mtd/mtd.h>
59 #ifdef CONFIG_MTD_PARTITIONS
60 #include <linux/mtd/partitions.h>
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 */
75 EXPORT_SYMBOL(bcm947xx_cpu_clk
);
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);
90 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
91 extern struct ide_ops std_ide_ops
;
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
));
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 */
110 if (sb_chip(sbh
) == BCM4785_CHIP_ID
) {
111 __asm__
__volatile__(
122 bcm947xx_machine_halt(void)
124 printk("System halted\n");
126 /* Disable interrupts and watchdog and spin forever */
134 static int ser_line
= 0;
143 static serial_port ports
[4];
144 static int num_ports
= 0;
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
;
157 do_serial_add(serial_port
*port
)
163 struct serial_struct s
;
167 baud_base
= port
->baud_base
;
168 reg_shift
= port
->reg_shift
;
170 memset(&s
, 0, sizeof(s
));
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 */
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");
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]);
219 for (i
= 0; i
< num_ports
; i
++)
220 do_serial_add(&ports
[i
]);
224 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
225 ide_ops
= &std_ide_ops
;
228 /* Override default command line arguments */
229 if (value
&& strlen(value
) && strncmp(value
, "empty", 5))
230 strncpy(arcs_cmdline
, value
, sizeof(arcs_cmdline
));
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
;
243 get_system_type(void)
248 sprintf(s
, "Broadcom BCM%X chip rev %d pkg %d", sb_chip(bcm947xx_sbh
),
249 sb_chiprev(bcm947xx_sbh
), sb_chippkg(bcm947xx_sbh
));
253 return "Broadcom BCM947XX";
262 #ifdef CONFIG_MTD_PARTITIONS
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);
276 if (boardnum
== 0 && boardtype
== 0x48E && boardrev
== 0x35) {
285 static size_t get_erasesize(struct mtd_info
*mtd
, size_t offset
, size_t size
)
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
;
303 erasesize
= mtd
->erasesize
;
308 #endif // DIR320_BOARD
311 new layout -- zzz 04/2006
315 +---+----------+ < search for HDR0
319 | i +----------+ < + trx->offset[1]
323 + +----------+ < + trx->len
325 +---+----------+ < size - NVRAM_SPACE - board_data_size()
327 +--------------+ < size - NVRAM_SPACE
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, },
344 #define PART_ROOTFS 2
349 struct mtd_partition
* __init
350 init_mtd_partitions(struct mtd_info
*mtd
, size_t size
)
353 struct trx_header
*trx
;
354 unsigned char buf
[512];
355 size_t off
, trxoff
, boardoff
;
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();
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
;
379 bcm947xx_parts
[PART_BOARD
].name
= NULL
;
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
) {
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
;
418 printk(KERN_NOTICE
"%s: Unable to find a valid linux partition\n", mtd
->name
);
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
);
432 return bcm947xx_parts
;
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;
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
) ||
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
;
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]);
478 /* romfs is at block zero too */
479 if (romfsb
->word0
== ROMSB_WORD0
&&
480 romfsb
->word1
== ROMSB_WORD1
) {
482 "%s: romfs filesystem found at block %d\n",
483 mtd
->name
, off
/ BLOCK_SIZE
);
487 /* squashfs is at block zero too */
488 if (squashfsb
->s_magic
== SQUASHFS_MAGIC
) {
490 "%s: squashfs filesystem found at block %d\n",
491 mtd
->name
, off
/ BLOCK_SIZE
);
496 if (cramfsb
->magic
== CRAMFS_MAGIC
) {
498 "%s: cramfs filesystem found at block %d\n",
499 mtd
->name
, off
/ BLOCK_SIZE
);
504 * Read block 1 to test for minix and ext2 superblock
506 if (MTD_READ(mtd
, off
+ BLOCK_SIZE
, sizeof(buf
), &len
, buf
) ||
511 if (minixsb
->s_magic
== MINIX_SUPER_MAGIC
||
512 minixsb
->s_magic
== MINIX_SUPER_MAGIC2
) {
514 "%s: Minix filesystem found at block %d\n",
515 mtd
->name
, off
/ BLOCK_SIZE
);
520 if (ext2sb
->s_magic
== cpu_to_le16(EXT2_SUPER_MAGIC
)) {
522 "%s: ext2 filesystem found at block %d\n",
523 mtd
->name
, off
/ BLOCK_SIZE
);
529 printk(KERN_NOTICE
"%s: Unable to find a valid linux partition\n", mtd
->name
);
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 */
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
;
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;
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;
566 bcm947xx_parts
[PART_ROOTFS
].size
= bcm947xx_parts
[PART_JFFS2
].offset
- bcm947xx_parts
[PART_ROOTFS
].offset
;
569 return bcm947xx_parts
;
573 EXPORT_SYMBOL(init_mtd_partitions
);