--- linux-2.6.22.14/arch/powerpc/kernel/Makefile 2008-01-22 13:59:14.000000000 +0200 +++ linux/arch/powerpc/kernel/Makefile 2008-01-15 10:59:50.000000000 +0200 @@ -76,8 +76,10 @@ obj-$(CONFIG_AUDIT) += audit.o obj64-$(CONFIG_AUDIT) += compat_audit.o ifneq ($(CONFIG_PPC_INDIRECT_IO),y) +ifneq ($(CONFIG_RB_IOMAP),y) obj-y += iomap.o endif +endif ifeq ($(CONFIG_PPC_ISERIES),y) extra-y += lparmap.s --- linux-2.6.22.14/arch/powerpc/kernel/pci_32.c 2008-01-22 13:59:14.000000000 +0200 +++ linux/arch/powerpc/kernel/pci_32.c 2008-01-15 10:59:50.000000000 +0200 @@ -1401,8 +1401,7 @@ char __init *pcibios_setup(char *str) } /* the next one is stolen from the alpha port... */ -void __init -pcibios_update_irq(struct pci_dev *dev, int irq) +void pcibios_update_irq(struct pci_dev *dev, int irq) { pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); /* XXX FIXME - update OF device tree node interrupt property */ @@ -1459,6 +1458,7 @@ int pci_read_irq_line(struct pci_dev *pc DBG(" -> failed to map !\n"); return -1; } + pcibios_update_irq(pci_dev, virq); pci_dev->irq = virq; return 0; --- linux-2.6.22.14/arch/powerpc/kernel/process.c 2008-01-22 13:59:14.000000000 +0200 +++ linux/arch/powerpc/kernel/process.c 2008-01-15 10:59:50.000000000 +0200 @@ -344,7 +344,7 @@ static void show_instructions(struct pt_ unsigned long pc = regs->nip - (instructions_to_print * 3 / 4 * sizeof(int)); - printk("Instruction dump:"); + printb("Instruction dump:"); for (i = 0; i < instructions_to_print; i++) { int instr; @@ -358,18 +358,18 @@ static void show_instructions(struct pt_ */ if (!__kernel_text_address(pc) || __get_user(instr, (unsigned int __user *)pc)) { - printk("XXXXXXXX "); + printb("XXXXXXXX "); } else { if (regs->nip == pc) - printk("<%08x> ", instr); + printb("<%08x> ", instr); else - printk("%08x ", instr); + printb("%08x ", instr); } pc += sizeof(int); } - printk("\n"); + printb("\n"); } static struct regbit { @@ -389,13 +389,13 @@ static void printbits(unsigned long val, { const char *sep = ""; - printk("<"); + printb("<"); for (; bits->bit; ++bits) if (val & bits->bit) { - printk("%s%s", sep, bits->name); + printb("%s%s", sep, bits->name); sep = ","; } - printk(">"); + printb(">"); } #ifdef CONFIG_PPC64 @@ -412,39 +412,39 @@ void show_regs(struct pt_regs * regs) { int i, trap; - printk("NIP: "REG" LR: "REG" CTR: "REG"\n", + printb("NIP: "REG" LR: "REG" CTR: "REG"\n", regs->nip, regs->link, regs->ctr); - printk("REGS: %p TRAP: %04lx %s (%s)\n", + printb("REGS: %p TRAP: %04lx %s (%s)\n", regs, regs->trap, print_tainted(), init_utsname()->release); - printk("MSR: "REG" ", regs->msr); + printb("MSR: "REG" ", regs->msr); printbits(regs->msr, msr_bits); - printk(" CR: %08lx XER: %08lx\n", regs->ccr, regs->xer); + printb(" CR: %08lx XER: %08lx\n", regs->ccr, regs->xer); trap = TRAP(regs); if (trap == 0x300 || trap == 0x600) - printk("DAR: "REG", DSISR: "REG"\n", regs->dar, regs->dsisr); - printk("TASK = %p[%d] '%s' THREAD: %p", + printb("DAR: "REG", DSISR: "REG"\n", regs->dar, regs->dsisr); + printb("TASK = %p[%d] '%s' THREAD: %p", current, current->pid, current->comm, task_thread_info(current)); #ifdef CONFIG_SMP - printk(" CPU: %d", smp_processor_id()); + printb(" CPU: %d", smp_processor_id()); #endif /* CONFIG_SMP */ for (i = 0; i < 32; i++) { if ((i % REGS_PER_LINE) == 0) - printk("\n" KERN_INFO "GPR%02d: ", i); - printk(REG " ", regs->gpr[i]); + printb("\n" KERN_INFO "GPR%02d: ", i); + printb(REG " ", regs->gpr[i]); if (i == LAST_VOLATILE && !FULL_REGS(regs)) break; } - printk("\n"); + printb("\n"); #ifdef CONFIG_KALLSYMS /* * Lookup NIP late so we have the best change of getting the * above info out without failing */ - printk("NIP ["REG"] ", regs->nip); + printb("NIP ["REG"] ", regs->nip); print_symbol("%s\n", regs->nip); - printk("LR ["REG"] ", regs->link); + printb("LR ["REG"] ", regs->link); print_symbol("%s\n", regs->link); #endif show_stack(current, (unsigned long *) regs->gpr[1]); @@ -920,7 +920,7 @@ void show_stack(struct task_struct *tsk, } lr = 0; - printk("Call Trace:\n"); + printb("Call Trace:\n"); do { if (!validate_sp(sp, tsk, MIN_STACK_FRAME)) return; @@ -929,11 +929,11 @@ void show_stack(struct task_struct *tsk, newsp = stack[0]; ip = stack[FRAME_LR_SAVE]; if (!firstframe || ip != lr) { - printk("["REG"] ["REG"] ", sp, ip); + printb("["REG"] ["REG"] ", sp, ip); print_symbol("%s", ip); if (firstframe) - printk(" (unreliable)"); - printk("\n"); + printb(" (unreliable)"); + printb("\n"); } firstframe = 0; @@ -945,7 +945,7 @@ void show_stack(struct task_struct *tsk, && stack[FRAME_MARKER] == REGS_MARKER) { struct pt_regs *regs = (struct pt_regs *) (sp + STACK_FRAME_OVERHEAD); - printk("--- Exception: %lx", regs->trap); + printb("--- Exception: %lx", regs->trap); print_symbol(" at %s\n", regs->nip); lr = regs->link; print_symbol(" LR = %s\n", lr); --- linux-2.6.22.14/arch/powerpc/kernel/prom.c 2008-01-22 13:59:14.000000000 +0200 +++ linux/arch/powerpc/kernel/prom.c 2008-01-15 10:59:50.000000000 +0200 @@ -759,6 +759,14 @@ static int __init early_init_dt_scan_cho (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) return 0; +#ifdef CONFIG_BLK_DEV_INITRD + lprop = of_get_flat_dt_prop(node, "linux,initrd", NULL); + if (lprop) { + initrd_start = (unsigned) __va(lprop[0]); + initrd_end = initrd_start + lprop[1]; + } +#endif + #ifdef CONFIG_PPC64 /* check if iommu is forced on or off */ if (of_get_flat_dt_prop(node, "linux,iommu-off", NULL) != NULL) @@ -1026,6 +1034,11 @@ void __init early_init_devtree(void *par of_scan_flat_dt(early_init_dt_scan_root, NULL); of_scan_flat_dt(early_init_dt_scan_memory, NULL); +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + lmb_reserve(__pa(initrd_start), initrd_end - initrd_start); +#endif + /* Save command line for /proc/cmdline and then parse parameters */ strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE); parse_early_param(); --- linux-2.6.22.14/arch/powerpc/kernel/traps.c 2008-01-22 13:59:14.000000000 +0200 +++ linux/arch/powerpc/kernel/traps.c 2008-01-15 10:59:50.000000000 +0200 @@ -126,25 +126,25 @@ int die(const char *str, struct pt_regs } if (++die.lock_owner_depth < 3) { - printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); + printb("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter); #ifdef CONFIG_PREEMPT - printk("PREEMPT "); + printb("PREEMPT "); #endif #ifdef CONFIG_SMP - printk("SMP NR_CPUS=%d ", NR_CPUS); + printb("SMP NR_CPUS=%d ", NR_CPUS); #endif #ifdef CONFIG_DEBUG_PAGEALLOC - printk("DEBUG_PAGEALLOC "); + printb("DEBUG_PAGEALLOC "); #endif #ifdef CONFIG_NUMA - printk("NUMA "); + printb("NUMA "); #endif - printk("%s\n", ppc_md.name ? ppc_md.name : ""); + printb("%s\n", ppc_md.name ? ppc_md.name : ""); print_modules(); show_regs(regs); } else { - printk("Recursive die() failure, output suppressed\n"); + printb("Recursive die() failure, output suppressed\n"); } bust_spinlocks(0); @@ -372,9 +372,9 @@ void machine_check_exception(struct pt_r printk("Data"); printk(" machine check in kernel mode.\n"); #elif defined(CONFIG_440A) - printk("Machine check in kernel mode.\n"); + printb("Machine check in kernel mode.\n"); if (reason & ESR_IMCP){ - printk("Instruction Synchronous Machine Check exception\n"); + printb("Instruction Synchronous Machine Check exception\n"); mtspr(SPRN_ESR, reason & ~ESR_IMCP); } else { @@ -402,82 +402,82 @@ void machine_check_exception(struct pt_r mtspr(SPRN_MCSR, mcsr); } #elif defined (CONFIG_E500) - printk("Machine check in kernel mode.\n"); - printk("Caused by (from MCSR=%lx): ", reason); + printb("Machine check in kernel mode.\n"); + printb("Caused by (from MCSR=%lx): ", reason); if (reason & MCSR_MCP) - printk("Machine Check Signal\n"); + printb("Machine Check Signal\n"); if (reason & MCSR_ICPERR) - printk("Instruction Cache Parity Error\n"); + printb("Instruction Cache Parity Error\n"); if (reason & MCSR_DCP_PERR) - printk("Data Cache Push Parity Error\n"); + printb("Data Cache Push Parity Error\n"); if (reason & MCSR_DCPERR) - printk("Data Cache Parity Error\n"); + printb("Data Cache Parity Error\n"); if (reason & MCSR_GL_CI) - printk("Guarded Load or Cache-Inhibited stwcx.\n"); + printb("Guarded Load or Cache-Inhibited stwcx.\n"); if (reason & MCSR_BUS_IAERR) - printk("Bus - Instruction Address Error\n"); + printb("Bus - Instruction Address Error\n"); if (reason & MCSR_BUS_RAERR) - printk("Bus - Read Address Error\n"); + printb("Bus - Read Address Error\n"); if (reason & MCSR_BUS_WAERR) - printk("Bus - Write Address Error\n"); + printb("Bus - Write Address Error\n"); if (reason & MCSR_BUS_IBERR) - printk("Bus - Instruction Data Error\n"); + printb("Bus - Instruction Data Error\n"); if (reason & MCSR_BUS_RBERR) - printk("Bus - Read Data Bus Error\n"); + printb("Bus - Read Data Bus Error\n"); if (reason & MCSR_BUS_WBERR) - printk("Bus - Read Data Bus Error\n"); + printb("Bus - Read Data Bus Error\n"); if (reason & MCSR_BUS_IPERR) - printk("Bus - Instruction Parity Error\n"); + printb("Bus - Instruction Parity Error\n"); if (reason & MCSR_BUS_RPERR) - printk("Bus - Read Parity Error\n"); + printb("Bus - Read Parity Error\n"); #elif defined (CONFIG_E200) - printk("Machine check in kernel mode.\n"); - printk("Caused by (from MCSR=%lx): ", reason); + printb("Machine check in kernel mode.\n"); + printb("Caused by (from MCSR=%lx): ", reason); if (reason & MCSR_MCP) - printk("Machine Check Signal\n"); + printb("Machine Check Signal\n"); if (reason & MCSR_CP_PERR) - printk("Cache Push Parity Error\n"); + printb("Cache Push Parity Error\n"); if (reason & MCSR_CPERR) - printk("Cache Parity Error\n"); + printb("Cache Parity Error\n"); if (reason & MCSR_EXCP_ERR) - printk("ISI, ITLB, or Bus Error on first instruction fetch for an exception handler\n"); + printb("ISI, ITLB, or Bus Error on first instruction fetch for an exception handler\n"); if (reason & MCSR_BUS_IRERR) - printk("Bus - Read Bus Error on instruction fetch\n"); + printb("Bus - Read Bus Error on instruction fetch\n"); if (reason & MCSR_BUS_DRERR) - printk("Bus - Read Bus Error on data load\n"); + printb("Bus - Read Bus Error on data load\n"); if (reason & MCSR_BUS_WRERR) - printk("Bus - Write Bus Error on buffered store or cache line push\n"); + printb("Bus - Write Bus Error on buffered store or cache line push\n"); #else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */ - printk("Machine check in kernel mode.\n"); - printk("Caused by (from SRR1=%lx): ", reason); + printb("Machine check in kernel mode.\n"); + printb("Caused by (from SRR1=%lx): ", reason); switch (reason & 0x601F0000) { case 0x80000: - printk("Machine check signal\n"); + printb("Machine check signal\n"); break; case 0: /* for 601 */ case 0x40000: case 0x140000: /* 7450 MSS error and TEA */ - printk("Transfer error ack signal\n"); + printb("Transfer error ack signal\n"); break; case 0x20000: - printk("Data parity error signal\n"); + printb("Data parity error signal\n"); break; case 0x10000: - printk("Address parity error signal\n"); + printb("Address parity error signal\n"); break; case 0x20000000: - printk("L1 Data Cache error\n"); + printb("L1 Data Cache error\n"); break; case 0x40000000: - printk("L1 Instruction Cache error\n"); + printb("L1 Instruction Cache error\n"); break; case 0x00100000: - printk("L2 data cache parity error\n"); + printb("L2 data cache parity error\n"); break; default: - printk("Unknown values in msr\n"); + printb("Unknown values in msr\n"); } #endif /* CONFIG_4xx */ @@ -861,7 +861,7 @@ void alignment_exception(struct pt_regs void StackOverflow(struct pt_regs *regs) { - printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", + printb(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", current, regs->gpr[1]); debugger(regs); show_regs(regs); --- linux-2.6.22.14/arch/powerpc/oprofile/common.c 2008-01-22 13:59:11.000000000 +0200 +++ linux/arch/powerpc/oprofile/common.c 2008-01-21 16:52:31.000000000 +0200 @@ -139,6 +139,8 @@ static int op_powerpc_create_files(struc int __init oprofile_arch_init(struct oprofile_operations *ops) { + ops->backtrace = op_powerpc_backtrace; + if (!cur_cpu_spec->oprofile_cpu_type) return -ENODEV; --- linux-2.6.22.14/arch/powerpc/platforms/83xx/Kconfig 2008-01-22 13:59:19.000000000 +0200 +++ linux/arch/powerpc/platforms/83xx/Kconfig 2008-01-15 10:59:57.000000000 +0200 @@ -23,6 +23,12 @@ config MPC832x_RDB help This option enables support for the MPC8323 RDB board. +config RB_PPC + bool "Mikrotik RB333/RB600" + select QUICC_ENGINE + help + This option enables support for the Mikrotik RB333/RB600 board. + config MPC834x_MDS bool "Freescale MPC834x MDS" select DEFAULT_UIMAGE @@ -63,6 +69,7 @@ config PPC_MPC832x select PPC_UDBG_16550 select PPC_INDIRECT_PCI default y if MPC832x_MDS || MPC832x_RDB + default y if RB_PPC config MPC834x bool --- linux-2.6.22.14/arch/powerpc/platforms/83xx/Makefile 2008-01-22 13:59:19.000000000 +0200 +++ linux/arch/powerpc/platforms/83xx/Makefile 2008-01-15 10:59:57.000000000 +0200 @@ -9,3 +9,4 @@ obj-$(CONFIG_MPC834x_MDS) += mpc834x_mds obj-$(CONFIG_MPC834x_ITX) += mpc834x_itx.o obj-$(CONFIG_MPC836x_MDS) += mpc836x_mds.o obj-$(CONFIG_MPC832x_MDS) += mpc832x_mds.o +obj-$(CONFIG_RB_PPC) += rb_ppc.o --- linux-2.6.22.14/arch/powerpc/platforms/83xx/rb_ppc.c 1970-01-01 03:00:00.000000000 +0300 +++ linux/arch/powerpc/platforms/83xx/rb_ppc.c 2008-01-21 16:18:34.000000000 +0200 @@ -0,0 +1,368 @@ +/* + * Copyright (C) Mikrotik 2007 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mpc83xx.h" + +#define SYSCTL 0x100 +#define SICRL 0x014 + +#define GTCFR2 0x04 +#define GTMDR4 0x22 +#define GTRFR4 0x26 +#define GTCNR4 0x2e +#define GTVER4 0x36 +#define GTPSR4 0x3e + +#define GTCFR_BCM 0x40 +#define GTCFR_STP4 0x20 +#define GTCFR_RST4 0x10 +#define GTCFR_STP3 0x02 +#define GTCFR_RST3 0x01 + +#define GTMDR_ORI 0x10 +#define GTMDR_FRR 0x08 +#define GTMDR_ICLK16 0x04 + +extern int par_io_data_set(u8 port, u8 pin, u8 val); +extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, + int assignment, int has_irq); + +static unsigned timer_freq; +static void *gtm; + +static int beeper_irq; +static unsigned beeper_gpio_pin[2]; + +irqreturn_t rbppc_timer_irq(int irq, void *ptr) +{ + static int toggle = 0; + + par_io_data_set(beeper_gpio_pin[0], beeper_gpio_pin[1], toggle); + toggle = !toggle; + + /* ack interrupt */ + out_be16(gtm + GTVER4, 3); + + return IRQ_HANDLED; +} + +void rbppc_beep(unsigned freq) +{ + unsigned gtmdr; + + if (freq > 5000) freq = 5000; + + if (!gtm) + return; + if (!freq) { + out_8(gtm + GTCFR2, GTCFR_STP4 | GTCFR_STP3); + return; + } + + out_8(gtm + GTCFR2, GTCFR_RST4 | GTCFR_STP3); + out_be16(gtm + GTPSR4, 255); + gtmdr = GTMDR_FRR | GTMDR_ICLK16; + if (beeper_irq != NO_IRQ) gtmdr |= GTMDR_ORI; + out_be16(gtm + GTMDR4, gtmdr); + out_be16(gtm + GTVER4, 3); + + out_be16(gtm + GTRFR4, timer_freq / 16 / 256 / freq / 2); + out_be16(gtm + GTCNR4, 0); +} +EXPORT_SYMBOL(rbppc_beep); + +static void __init rbppc_setup_arch(void) +{ + struct device_node *np; + + np = of_find_node_by_type(NULL, "cpu"); + if (np) { + const unsigned *fp = get_property(np, "clock-frequency", NULL); + loops_per_jiffy = fp ? *fp / HZ : 0; + + of_node_put(np); + } + + np = of_find_node_by_name(NULL, "serial"); + if (np) { + timer_freq = + *(unsigned *) get_property(np, "clock-frequency", NULL); + of_node_put(np); + } + +#ifdef CONFIG_PCI + np = of_find_node_by_type(np, "pci"); + if (np) { + add_bridge(np); + ppc_md.pci_exclude_device = mpc83xx_exclude_device; + } +#endif + +#ifdef CONFIG_QUICC_ENGINE + np = of_find_node_by_name(np, "par_io"); + if (np) { + qe_reset(); + par_io_init(np); + of_node_put(np); + + np = NULL; + while (1) { + np = of_find_node_by_name(np, "ucc"); + if (!np) break; + + par_io_of_config(np); + } + } +#endif +} + +void __init rbppc_init_irq(void) +{ + struct device_node *np; + + np = of_find_node_by_type(NULL, "ipic"); + if (np) { + ipic_init(np, 0); + ipic_set_default_priority(); + of_node_put(np); + } + +#ifdef CONFIG_QUICC_ENGINE + np = of_find_node_by_type(NULL, "qeic"); + if (np) { + qe_ic_init(np, 0); + of_node_put(np); + } +#endif +} + +static int __init rbppc_probe(void) +{ + char *model; + + model = of_get_flat_dt_prop(of_get_flat_dt_root(), "model", NULL); + + if (!model) + return 0; + + if (strcmp(model, "RB333") == 0) + return 1; + + if (strcmp(model, "RB600") == 0) + return 1; + + return 0; +} + +static void __init rbppc_beeper_init(struct device_node *beeper) +{ + + struct resource res; + struct device_node *gpio; + const unsigned *pin; + const unsigned *gpio_id; + + if (of_address_to_resource(beeper, 0, &res)) { + printk("beeper error: no region specified\n"); + return; + } + + pin = get_property(beeper, "gpio", NULL); + if (pin) { + gpio = of_find_node_by_phandle(pin[0]); + + if (!gpio) { + printk("beeper error: gpio handle %x not found\n", + pin[0]); + return; + } + + gpio_id = get_property(gpio, "device-id", NULL); + if (!gpio_id) { + printk("beeper error: no device-id specified" + " in gpio\n"); + return; + } + + beeper_gpio_pin[0] = *gpio_id; + beeper_gpio_pin[1] = pin[1]; + + par_io_config_pin(*gpio_id, pin[1], 1, 0, 0, 0); + } else { + void *sysctl; + + sysctl = ioremap_nocache(get_immrbase() + SYSCTL, 0x100); + out_be32(sysctl + SICRL, + in_be32(sysctl + SICRL) | (1 << (31 - 19))); + iounmap(sysctl); + } + + gtm = ioremap_nocache(res.start, res.end - res.start + 1); + + beeper_irq = irq_of_parse_and_map(beeper, 0); + if (beeper_irq != NO_IRQ) { + int e = request_irq(beeper_irq, rbppc_timer_irq, 0, "beeper", NULL); + if (e) printk(KERN_ERR "Request of beeper irq failed!\n"); + } +} + +#define SBIT(x) (0x80000000 >> (x)) +#define DBIT(x, y) ((y) << (32 - (((x % 16) + 1) * 2))) + +// rb300 +#define GPIO_DIR_RB300 (reg + (0x1408 >> 2)) +#define GPIO_DATA_RB300 (reg + (0x1404 >> 2)) + +// rb600 +#define SICRL_RB600 (reg + (0x114 >> 2)) +#define GPIO_DIR_RB600 (reg + (0xc00 >> 2)) +#define GPIO_DATA_RB600 (reg + (0xc08 >> 2)) + +static void rb_restart(char *cmd) +{ + unsigned rb_model; + __be32 __iomem *reg; + struct device_node *root; + unsigned int size; + + root = of_find_node_by_path("/"); + if (root) { + const char *prop = (char *) get_property(root, "model", &size); + rb_model = prop[sizeof("RB") - 1] - '0'; + of_node_put(root); + switch (rb_model) { + case 3: + reg = ioremap(get_immrbase(), 0x2000); + local_irq_disable(); + out_be32(GPIO_DIR_RB300, + (in_be32(GPIO_DIR_RB300) & ~DBIT(4, 3)) | DBIT(4, 1)); + out_be32(GPIO_DATA_RB300, in_be32(GPIO_DATA_RB300) & ~SBIT(4)); + break; + case 6: + reg = ioremap(get_immrbase(), 0x1000); + local_irq_disable(); + out_be32(SICRL_RB600, in_be32(SICRL_RB600) & ~0x00800000); + out_be32(GPIO_DIR_RB600, in_be32(GPIO_DIR_RB600) | SBIT(2)); + out_be32(GPIO_DATA_RB600, in_be32(GPIO_DATA_RB600) & ~SBIT(2)); + break; + default: + mpc83xx_restart(cmd); + break; + } + } + else mpc83xx_restart(cmd); + + for (;;) ; +} + +static struct of_device_id rbppc_ids[] = { + { .type = "soc", }, + { .compatible = "soc", }, + { .type = "qe", }, + { .type = "mdio", }, + {}, +}; + +static int __init rbppc_declare_of_platform_devices(void) +{ + struct device_node *np; + unsigned idx; + + of_platform_bus_probe(NULL, rbppc_ids, NULL); + + /* fix MDIO region */ + np = of_find_node_by_type(NULL, "mdio"); + if (np) { + unsigned len; + unsigned *res; + const unsigned *eres; + struct device_node *ep; + + ep = of_find_compatible_node(NULL, "network", "ucc_geth"); + if (ep) { + eres = of_get_property(ep, "reg", &len); + res = (unsigned *) of_get_property(np, "reg", &len); + if (res && eres) + res[0] = eres[0] + 0x120;; + } + } + + np = of_find_node_by_name(NULL, "nand"); + if (np) of_platform_device_create(np, "nand", NULL); + + idx = 0; + for_each_node_by_type(np, "rb,cf") { + char dev_name[12]; + snprintf(dev_name, sizeof(dev_name), "cf.%u", idx); + of_platform_device_create(np, dev_name, NULL); + ++idx; + } + + np = of_find_node_by_name(NULL, "beeper"); + if (np) rbppc_beeper_init(np); + + return 0; +} +device_initcall(rbppc_declare_of_platform_devices); + +static void rb_halt(void) +{ + while (1); +} + +define_machine(rb333) { + .name = "RB333/RB600", + .probe = rbppc_probe, + .setup_arch = rbppc_setup_arch, + .init_IRQ = rbppc_init_irq, + .get_irq = ipic_get_irq, + .restart = rb_restart, + .halt = rb_halt, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, +}; + +static void fixup_pcibridge(struct pci_dev *dev) +{ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + /* let the kernel itself set right memory windows */ + pci_write_config_word(dev, PCI_MEMORY_BASE, 0); + pci_write_config_word(dev, PCI_MEMORY_LIMIT, 0); + pci_write_config_word(dev, PCI_PREF_MEMORY_BASE, 0); + pci_write_config_word(dev, PCI_PREF_MEMORY_LIMIT, 0); + pci_write_config_byte(dev, PCI_IO_BASE, 0); + pci_write_config_byte(dev, PCI_IO_LIMIT, 4 << 4); + + pci_write_config_byte( + dev, PCI_COMMAND, + PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8); + } +} + +static void fixup_rb604(struct pci_dev *dev) +{ + pci_write_config_byte(dev, 0xC0, 0x01); +} + +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, fixup_pcibridge) +DECLARE_PCI_FIXUP_HEADER(0x3388, 0x0021, fixup_rb604) --- linux-2.6.22.14/arch/powerpc/platforms/85xx/Kconfig 2008-01-22 13:59:19.000000000 +0200 +++ linux/arch/powerpc/platforms/85xx/Kconfig 2008-01-15 10:59:57.000000000 +0200 @@ -34,13 +34,18 @@ config MPC8544_DS help This option enables support for the MPC8544 DS board +config RB1000 + bool "Mikrotik RB1000" + help + This option enables support for the Mikrotik RB1000 board + endchoice config MPC8540 bool select PPC_UDBG_16550 select PPC_INDIRECT_PCI - default y if MPC8540_ADS || MPC85xx_CDS + default y if MPC8540_ADS || MPC85xx_CDS || RB1000 config MPC8560 bool @@ -55,4 +60,4 @@ config MPC85xx select MPIC select SERIAL_8250_SHARE_IRQ if SERIAL_8250 default y if MPC8540_ADS || MPC85xx_CDS || MPC8560_ADS \ - || MPC85xx_MDS || MPC8544_DS + || MPC85xx_MDS || MPC8544_DS || RB1000 --- linux-2.6.22.14/arch/powerpc/platforms/85xx/Makefile 2008-01-22 13:59:19.000000000 +0200 +++ linux/arch/powerpc/platforms/85xx/Makefile 2008-01-15 10:59:57.000000000 +0200 @@ -7,3 +7,4 @@ obj-$(CONFIG_MPC8560_ADS) += mpc85xx_ads obj-$(CONFIG_MPC85xx_CDS) += mpc85xx_cds.o obj-$(CONFIG_MPC8544_DS) += mpc8544_ds.o obj-$(CONFIG_MPC85xx_MDS) += mpc85xx_mds.o +obj-$(CONFIG_RB1000) += rb1000.o --- linux-2.6.22.14/arch/powerpc/platforms/85xx/rb1000.c 1970-01-01 03:00:00.000000000 +0300 +++ linux/arch/powerpc/platforms/85xx/rb1000.c 2008-01-15 10:59:57.000000000 +0200 @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mpc85xx.h" + +#ifdef MT_DEBUG +static int inited = 0; + +void rb1000_putc(char c) +{ + if (!inited) return; + + while (!(*(volatile unsigned char *) 0xf0004505 & 0x20)); + + *(char *) 0xf0004500 = c; +} + +void rb1000_puts(char *str) +{ + while (*str) { + if (*str == '\n') rb1000_putc('\r'); + rb1000_putc(*str); + ++str; + } +} + +void rb1000_printk(const char *fmt, ...) { + va_list args; + int r; + char buf[256]; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + rb1000_puts(buf); +} + +void rb1000_init(void) +{ + if (inited) return; + + settlbcam(3, 0xf0000000, 0xe0000000, 0x10000, _PAGE_IO, 0); + inited = 1; +} +#endif + +#define GPIO(x) (0x80000000 >> (x)) +static unsigned *gpio_data = NULL; +static unsigned *picr = NULL; + +const unsigned *beep1; +const unsigned *beep2; + +#define GT0_BASE_COUNT (picr + (0x1110 / 4)) + +static void ioremap_from_node(const unsigned *property, unsigned **ptr) { + struct resource res; + struct device_node *nd; + + nd = of_find_node_by_phandle(property[0]); + if (!nd || of_address_to_resource(nd, 0, &res)) return; + of_node_put(nd); + + *ptr = ioremap_nocache(res.start, res.end - res.start + 1); +} + +irqreturn_t beeper_irq(int irq, void *ptr) +{ + static int toggle = 1; + if (toggle) { + out_be32(gpio_data, in_be32(gpio_data) & ~GPIO(beep1[0])); + out_be32(gpio_data, in_be32(gpio_data) & ~GPIO(beep2[0])); + } + else { + out_be32(gpio_data, in_be32(gpio_data) | GPIO(beep1[0])); + out_be32(gpio_data, in_be32(gpio_data) | GPIO(beep2[0])); + } + toggle ^= 1; + return IRQ_HANDLED; +} + +void consume(int x) { } + +extern unsigned long ppc_tb_freq; +static unsigned long ppc_tb_freq_kHz; + +static void __init rb1000_beeper_init(struct device_node *beeper) +{ + unsigned interrupt; + const unsigned *int_p; + const unsigned *gpio; + + printk("beeper init\n"); + + beep1 = get_property(beeper, "beep1", NULL); + beep2 = get_property(beeper, "beep2", NULL); + gpio = get_property(beeper, "gpio", NULL); + int_p = get_property(beeper, "interrupt-parent", NULL); + + ioremap_from_node(gpio, &gpio_data); + ioremap_from_node(int_p, &picr); + + ppc_tb_freq_kHz = (ppc_tb_freq / 1000); + + interrupt = irq_of_parse_and_map(beeper, 0); + printk("irq=%u\n", interrupt); + if (interrupt != NO_IRQ) { + consume(request_irq(interrupt, beeper_irq, + IRQF_TRIGGER_RISING, + "beeper", NULL)); + } + + printk("beeper done\n"); +} + +void rbppc_beep(unsigned freq) { + if (freq) { + out_be32(GT0_BASE_COUNT, (500 * ppc_tb_freq_kHz) / freq); + } + else { + out_be32(GT0_BASE_COUNT, 0x80000000); + } +} +EXPORT_SYMBOL(rbppc_beep); + +static void __init rb1000_pic_init(void) +{ + struct device_node *np; + struct resource r; + struct mpic *mpic; + void *gcr; + unsigned i; + + np = of_find_node_by_type(NULL, "open-pic"); + + if (!np) + return; + + if (of_address_to_resource(np, 0, &r)) { + printk(KERN_ERR "mpic error: no region specified\n"); + of_node_put(np); + return; + } + + gcr = ioremap(r.start + 0x1020, 4); + out_be32(gcr, in_be32(gcr) | (1 << 29)); + iounmap(gcr); + + mpic = mpic_alloc(np, r.start, + MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN, + 1, 0, " OpenPIC "); + of_node_put(np); + + for (i = 0; i < 31; ++i) { + if (i == 11 || i == 12) { + /* Ext IRQ4 and IRQ5 is mapped to 11 & 12 respectively */ + mpic_assign_isu(mpic, i, r.start + 0x10000 + (i - 11 + 4) * 0x20); + } else if (i == 30) { + mpic_assign_isu(mpic, i, r.start + 0x10000 + 7 * 0x20); + } else { + mpic_assign_isu(mpic, i, r.start + 0x10200 + i * 0x20); + } + } + mpic_assign_isu(mpic, 31, r.start + 0x1120); + mpic_init(mpic); +} + +static void __init rb1000_setup_arch(void) +{ + struct device_node *np; + + mtspr(SPRN_HID0, 1 << 14); /* set TBEN */ + mb(); + + np = of_find_node_by_type(NULL, "cpu"); + if (np) { + const unsigned *fp = get_property(np, "clock-frequency", NULL); + loops_per_jiffy = fp ? *fp / HZ : 0; + + of_node_put(np); + } +} + +static void rb1000_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "Vendor\t\t: Mikrotik\n"); + seq_printf(m, "Machine\t\t: RB1000\n"); + seq_printf(m, "Memory\t\t: %lu MB\n", total_memory / (1024 * 1024)); +} + +static int __init rb1000_probe(void) +{ + char *model; + + model = of_get_flat_dt_prop(of_get_flat_dt_root(), "model", NULL); + + if (!model) + return 0; + + if (strcmp(model, "RB1000") == 0) + return 1; + + return 0; +} + +static int __init rb1000_declare_of_platform_devices(void) +{ + struct device_node *beep_np; + struct device_node *np; + unsigned idx; + + np = of_find_node_by_name(NULL, "nand"); + if (np) of_platform_device_create(np, "nand", NULL); + + idx = 0; + for_each_node_by_type(np, "rb,cf") { + char dev_name[12]; + snprintf(dev_name, sizeof(dev_name), "cf.%u", idx); + of_platform_device_create(np, dev_name, NULL); + ++idx; + } + + beep_np = of_find_node_by_name(NULL, "beeper"); + if (beep_np) rb1000_beeper_init(beep_np); + + return 0; +} +device_initcall(rb1000_declare_of_platform_devices); + +define_machine(mpc85xx_ads) { + .name = "RB1000", + .probe = rb1000_probe, + .setup_arch = rb1000_setup_arch, + .init_IRQ = rb1000_pic_init, + .show_cpuinfo = rb1000_show_cpuinfo, + .get_irq = mpic_get_irq, + .restart = mpc85xx_restart, + .calibrate_decr = generic_calibrate_decr, +}; --- linux-2.6.22.14/arch/powerpc/platforms/Kconfig 2008-01-22 13:59:20.000000000 +0200 +++ linux/arch/powerpc/platforms/Kconfig 2008-01-15 10:59:58.000000000 +0200 @@ -149,6 +149,10 @@ config GENERIC_IOMAP bool default n +config RB_IOMAP + bool + default y if RB_PPC || RB1000 + source "drivers/cpufreq/Kconfig" menu "CPU Frequency drivers" --- linux-2.6.22.14/arch/powerpc/sysdev/Makefile 2008-01-22 13:59:23.000000000 +0200 +++ linux/arch/powerpc/sysdev/Makefile 2008-01-15 11:00:00.000000000 +0200 @@ -34,3 +34,6 @@ obj-$(CONFIG_CPM2) += cpm2_common.o cpm obj-$(CONFIG_8xx) += mpc8xx_pic.o commproc.o obj-$(CONFIG_UCODE_PATCH) += micropatch.o endif + +obj-$(CONFIG_RB_IOMAP) += rb_iomap.o + --- linux-2.6.22.14/arch/powerpc/sysdev/qe_lib/qe.c 2008-01-22 13:59:23.000000000 +0200 +++ linux/arch/powerpc/sysdev/qe_lib/qe.c 2008-01-15 11:00:00.000000000 +0200 @@ -251,7 +251,7 @@ static int qe_sdma_init(void) /* allocate 2 internal temporary buffers (512 bytes size each) for * the SDMA */ - sdma_buf_offset = qe_muram_alloc(512 * 2, 4096); + sdma_buf_offset = qe_muram_alloc(512 * 2, 64); if (IS_ERR_VALUE(sdma_buf_offset)) return -ENOMEM; @@ -269,7 +269,7 @@ static DEFINE_SPINLOCK(qe_muram_lock); /* 16 blocks should be enough to satisfy all requests * until the memory subsystem goes up... */ -static rh_block_t qe_boot_muram_rh_block[16]; +static rh_block_t qe_boot_muram_rh_block[64]; static rh_info_t qe_muram_info; static void qe_muram_init(void) --- linux-2.6.22.14/arch/powerpc/sysdev/rb_iomap.c 1970-01-01 03:00:00.000000000 +0300 +++ linux/arch/powerpc/sysdev/rb_iomap.c 2008-01-15 11:00:00.000000000 +0200 @@ -0,0 +1,223 @@ +#include +#include +#include +#include + +#define LOCALBUS_START 0x40000000 +#define LOCALBUS_MASK 0x007fffff +#define LOCALBUS_REGMASK 0x001fffff + +static void __iomem *localbus_base; + +static inline int is_localbus(void __iomem *addr) +{ + return ((unsigned) addr & ~LOCALBUS_MASK) == LOCALBUS_START; +} + +static inline unsigned localbus_regoff(unsigned reg) { + return (reg << 16) | (((reg ^ 8) & 8) << 17); +} + +static inline void __iomem *localbus_addr(void __iomem *addr) +{ + return localbus_base + + ((unsigned) addr & LOCALBUS_MASK & ~LOCALBUS_REGMASK) + + localbus_regoff((unsigned) addr & LOCALBUS_REGMASK); +} + +unsigned int ioread8(void __iomem *addr) +{ + if (is_localbus(addr)) + return in_be16(localbus_addr(addr)) >> 8; + return readb(addr); +} +EXPORT_SYMBOL(ioread8); + +unsigned int ioread16(void __iomem *addr) +{ + if (is_localbus(addr)) + return le16_to_cpu(in_be16(localbus_addr(addr))); + return readw(addr); +} +EXPORT_SYMBOL(ioread16); + +unsigned int ioread16be(void __iomem *addr) +{ + return in_be16(addr); +} +EXPORT_SYMBOL(ioread16be); + +unsigned int ioread32(void __iomem *addr) +{ + return readl(addr); +} +EXPORT_SYMBOL(ioread32); + +unsigned int ioread32be(void __iomem *addr) +{ + return in_be32(addr); +} +EXPORT_SYMBOL(ioread32be); + +void iowrite8(u8 val, void __iomem *addr) +{ + if (is_localbus(addr)) + out_be16(localbus_addr(addr), ((u16) val) << 8); + else + writeb(val, addr); +} +EXPORT_SYMBOL(iowrite8); + +void iowrite16(u16 val, void __iomem *addr) +{ + if (is_localbus(addr)) + out_be16(localbus_addr(addr), cpu_to_le16(val)); + else + writew(val, addr); +} +EXPORT_SYMBOL(iowrite16); + +void iowrite16be(u16 val, void __iomem *addr) +{ + out_be16(addr, val); +} +EXPORT_SYMBOL(iowrite16be); + +void iowrite32(u32 val, void __iomem *addr) +{ + writel(val, addr); +} +EXPORT_SYMBOL(iowrite32); + +void iowrite32be(u32 val, void __iomem *addr) +{ + out_be32(addr, val); +} +EXPORT_SYMBOL(iowrite32be); + +void ioread8_rep(void __iomem *addr, void *dst, unsigned long count) +{ + if (is_localbus(addr)) { + unsigned i; + void *laddr = localbus_addr(addr); + u8 *buf = dst; + + for (i = 0; i < count; ++i) { + *buf++ = in_be16(laddr) >> 8; + } + } else { + _insb((u8 __iomem *) addr, dst, count); + } +} +EXPORT_SYMBOL(ioread8_rep); + +void ioread16_rep(void __iomem *addr, void *dst, unsigned long count) +{ + if (is_localbus(addr)) { + unsigned i; + void *laddr = localbus_addr(addr); + u16 *buf = dst; + + for (i = 0; i < count; ++i) { + *buf++ = in_be16(laddr); + } + } else { + _insw_ns((u16 __iomem *) addr, dst, count); + } +} +EXPORT_SYMBOL(ioread16_rep); + +void ioread32_rep(void __iomem *addr, void *dst, unsigned long count) +{ + _insl_ns((u32 __iomem *) addr, dst, count); +} +EXPORT_SYMBOL(ioread32_rep); + +void iowrite8_rep(void __iomem *addr, const void *src, unsigned long count) +{ + if (is_localbus(addr)) { + unsigned i; + void *laddr = localbus_addr(addr); + const u8 *buf = src; + + for (i = 0; i < count; ++i) { + out_be16(laddr, ((u16) *buf++) << 8); + } + } else { + _outsb((u8 __iomem *) addr, src, count); + } +} +EXPORT_SYMBOL(iowrite8_rep); + +void iowrite16_rep(void __iomem *addr, const void *src, unsigned long count) +{ + if (is_localbus(addr)) { + unsigned i; + void *laddr = localbus_addr(addr); + const u16 *buf = src; + + for (i = 0; i < count; ++i) { + out_be16(laddr, *buf++); + } + } else { + _outsw_ns((u16 __iomem *) addr, src, count); + } +} +EXPORT_SYMBOL(iowrite16_rep); + +void iowrite32_rep(void __iomem *addr, const void *src, unsigned long count) +{ + _outsl_ns((u32 __iomem *) addr, src, count); +} +EXPORT_SYMBOL(iowrite32_rep); + +void __iomem *ioport_map(unsigned long port, unsigned int len) +{ + return (void __iomem *) (port + _IO_BASE); +} +EXPORT_SYMBOL(ioport_unmap); + +void ioport_unmap(void __iomem *addr) +{ + /* Nothing to do */ +} +EXPORT_SYMBOL(ioport_map); + +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max) +{ + unsigned long start = pci_resource_start(dev, bar); + unsigned long len = pci_resource_len(dev, bar); + unsigned long flags = pci_resource_flags(dev, bar); + + if (!len) + return NULL; + if (max && len > max) + len = max; + if (flags & IORESOURCE_IO) + return ioport_map(start, len); + if (flags & IORESOURCE_MEM) + return ioremap(start, len); + /* What? */ + return NULL; +} +EXPORT_SYMBOL(pci_iomap); + +void pci_iounmap(struct pci_dev *dev, void __iomem *addr) +{ + /* Nothing to do */ +} +EXPORT_SYMBOL(pci_iounmap); + +void __iomem *localbus_map(unsigned long addr, unsigned int len) +{ + if (!localbus_base) + localbus_base = ioremap(addr & ~LOCALBUS_MASK, + LOCALBUS_MASK + 1); + return (void *) (LOCALBUS_START + (addr & LOCALBUS_MASK)); +} +EXPORT_SYMBOL(localbus_map); + +void localbus_unmap(void __iomem *addr) +{ +} +EXPORT_SYMBOL(localbus_unmap); --- linux-2.6.22.14/drivers/mtd/nand/Kconfig 2008-01-22 14:01:05.000000000 +0200 +++ linux/drivers/mtd/nand/Kconfig 2008-01-15 11:01:54.000000000 +0200 @@ -280,5 +280,16 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. +config MTD_NAND_RB500 + bool "Support for RB500/RB100 nand" + depends on MTD_NAND && MTD_PARTITIONS && MIPS_MIKROTIK + +config MTD_NAND_RB400 + bool "Support for RB400 nand" + depends on MTD_NAND && MTD_PARTITIONS && MIPS_MIKROTIK + +config MTD_NAND_RB_PPC + bool "Support for RB333 nand" + depends on MTD_NAND && MTD_PARTITIONS && (RB_PPC || RB1000) endif # MTD_NAND --- linux-2.6.22.14/drivers/mtd/nand/Makefile 2008-01-22 14:01:05.000000000 +0200 +++ linux/drivers/mtd/nand/Makefile 2008-01-18 13:06:54.000000000 +0200 @@ -27,5 +27,8 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nan obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o +obj-$(CONFIG_MTD_NAND_RB500) += rb500nand.o +obj-$(CONFIG_MTD_NAND_RB400) += rb400_nand.o +obj-$(CONFIG_MTD_NAND_RB_PPC) += rb_ppc.o nand-objs := nand_base.o nand_bbt.o --- linux-2.6.22.14/drivers/mtd/nand/nand_base.c 2008-01-22 14:01:05.000000000 +0200 +++ linux/drivers/mtd/nand/nand_base.c 2008-01-15 11:01:54.000000000 +0200 @@ -64,10 +64,12 @@ static struct nand_ecclayout nand_oob_8 static struct nand_ecclayout nand_oob_16 = { .eccbytes = 6, - .eccpos = {0, 1, 2, 3, 6, 7}, + .eccpos = {8, 9, 10, 13, 14, 15}, .oobfree = { - {.offset = 8, - . length = 8}} + {.offset = 0, + .length = 8}, + {.offset = 11, + .length = 2}} }; static struct nand_ecclayout nand_oob_64 = { @@ -430,6 +432,60 @@ void nand_wait_ready(struct mtd_info *mt } EXPORT_SYMBOL_GPL(nand_wait_ready); +static int get_backup_page(struct mtd_info *mtd, int page, int allow_bad) { +#ifdef MIPSEL + struct nand_chip *chip = mtd->priv; + int page_offset = chip->backup_offset >> chip->page_shift; + + if (page < page_offset) { + int page_backup = page + page_offset; + loff_t ofs_backup = ((loff_t) page_backup) << chip->page_shift; + if (allow_bad || !nand_block_checkbad(mtd, ofs_backup, 0, 1)) { + return page_backup; + } + } +#endif + return -1; +} + +static void nand_backup_merge(struct mtd_info *mtd, uint8_t *orig, int len) { + for ( ; len > 0; --len, ++orig) { + uint8_t byte = nand_read_byte(mtd); + *orig &= byte; + } +} + +static int check_backup_oob(struct mtd_info *mtd, struct nand_chip *chip, + int bpage) { + int differ = 0; + uint8_t ff = 0xff; + uint8_t *optr; + uint8_t *oend; + + /* check that OOB areas are equal or backup is empty */ + *(uint32_t*)(&chip->oob_poi[BACKUP_4xFF_OFFSET]) = 0xffffffff; + nand_wait_ready(mtd); + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, bpage); + + optr = chip->oob_poi; + oend = optr + mtd->oobsize; + for ( ; optr < oend; ++optr) { + uint8_t byte = nand_read_byte(mtd); + ff &= byte; + if (byte != *optr) { + *optr &= byte; + differ = 1; + } + } + return differ && ff != 0xff; +} + +static int read_page_raw_dummy(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + return 0; +} + /** * nand_command - [DEFAULT] Send command to NAND device * @mtd: MTD device structure @@ -980,6 +1036,7 @@ static int nand_do_read_ops(struct mtd_i /* Is the current page in the buffer ? */ if (realpage != chip->pagebuf || oob) { + int bpage = get_backup_page(mtd, page, 0); bufpoi = aligned ? buf : chip->buffers->databuf; if (likely(sndcmd)) { @@ -992,6 +1049,31 @@ static int nand_do_read_ops(struct mtd_i ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi); + + if (bpage >= 0) { + int use_backup = (mtd->ecc_stats.failed + - stats.failed + + mtd->ecc_stats.corrected + - stats.corrected); + if (!use_backup) { + use_backup = check_backup_oob(mtd, chip, + bpage); + } + sndcmd = 1; + if (use_backup) { + void *rptr; + chip->cmdfunc(mtd, NAND_CMD_READ0, + 0x00, bpage); + nand_backup_merge(mtd, bufpoi, + mtd->writesize); + + rptr = chip->ecc.read_page_raw; + chip->ecc.read_page_raw = read_page_raw_dummy; + ret = chip->ecc.read_page(mtd, chip, bufpoi); + chip->ecc.read_page_raw = rptr; + } + } + if (ret < 0) break; @@ -1007,7 +1089,8 @@ static int nand_do_read_ops(struct mtd_i /* Raw mode does data:oob:data:oob */ if (ops->mode != MTD_OOB_RAW) { int toread = min(oobreadlen, - chip->ecc.layout->oobavail); + ops->mode == MTD_OOB_PLACE ? + mtd->oobsize : chip->ecc.layout->oobavail); if (toread) { oob = nand_transfer_oob(chip, oob, ops, toread); @@ -1121,11 +1204,20 @@ static int nand_read(struct mtd_info *mt static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { + int bpage = get_backup_page(mtd, page, 0); + if (sndcmd) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); sndcmd = 0; } chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (bpage >= 0) { + nand_wait_ready(mtd); + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, bpage); + nand_backup_merge(mtd, chip->oob_poi, mtd->oobsize); + sndcmd = 1; + } return sndcmd; } @@ -1513,7 +1605,22 @@ static int nand_write_page(struct mtd_in const uint8_t *buf, int page, int cached, int raw) { int status; + int bpage = get_backup_page(mtd, page, 1); + if (bpage >= 0) { + loff_t bofs = ((loff_t) bpage) << chip->page_shift; + if (nand_block_checkbad(mtd, bofs, 0, 1)) { +#if 0 + printk(KERN_INFO "write to page 0x%08x" + ", for which backup is bad\n", + page); +#endif + return -EIO; + } + *(uint32_t*)(&chip->oob_poi[BACKUP_4xFF_OFFSET]) = 0; + *(uint32_t*)(&chip->oob_poi[ALWAYS_4x00_OFFSET]) = 0; + } +write_again: chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); if (unlikely(raw)) @@ -1553,6 +1660,16 @@ static int nand_write_page(struct mtd_in if (chip->verify_buf(mtd, buf, mtd->writesize)) return -EIO; #endif + if (bpage >= 0) { + page = bpage; + bpage = -2; + *(uint32_t*)(&chip->oob_poi[BACKUP_4xFF_OFFSET]) = 0xffffffff; + if (chip->ecc.mode == NAND_ECC_SOFT) { + // ECC is already calculated, do not recalculate it + raw = 1; + } + goto write_again; + } return 0; } @@ -1927,6 +2044,7 @@ int nand_erase_nand(struct mtd_info *mtd int allowbbt) { int page, len, status, pages_per_block, ret, chipnr; + int erase_bad = 0; struct nand_chip *chip = mtd->priv; int rewrite_bbt[NAND_MAX_CHIPS]={0}; unsigned int bbt_masked_page = 0xffffffff; @@ -1940,6 +2058,11 @@ int nand_erase_nand(struct mtd_info *mtd return -EINVAL; } + if (instr->len == 0xDeadBeef) { + instr->len = (1 << chip->phys_erase_shift); + erase_bad = 1; + } + /* Length must align on block boundary */ if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: " @@ -1992,6 +2115,18 @@ int nand_erase_nand(struct mtd_info *mtd instr->state = MTD_ERASING; while (len) { + int opage = page; + int bpage = get_backup_page(mtd, page, 1); + erase_again: + /* + * try to erase bad block + */ + if (erase_bad && chip->bbt) { + loff_t ofs = ((loff_t) page) << chip->page_shift; + int block = ofs >> chip->bbt_erase_shift; + chip->bbt[block / 4] &= ~(0x03 << ((block & 0x03) * 2)); + } + /* * heck if we have a bad block, we do not erase bad blocks ! */ @@ -2040,6 +2175,15 @@ int nand_erase_nand(struct mtd_info *mtd (page & BBT_PAGE_MASK) == bbt_masked_page) rewrite_bbt[chipnr] = (page << chip->page_shift); + /* erase backup page as well */ + if (bpage >= 0) { + if (page != bpage) { + page = bpage; + goto erase_again; + } + page = opage; + } + /* Increment page address and decrement length */ len -= (1 << chip->phys_erase_shift); page += pages_per_block; @@ -2345,6 +2489,32 @@ static struct nand_flash_dev *nand_get_f return type; } +#ifdef MIPSEL +void nand_read_id(struct mtd_info *mtd, unsigned char *ids) +{ + struct nand_chip *chip = mtd->priv; + if (chip == NULL) { + /* this is partition - get a master */ + mtd = *(struct mtd_info **)(mtd + 1); + chip = mtd->priv; + } + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd , FL_READING); + + chip->select_chip(mtd, 0); + chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + ids[0] = chip->read_byte(mtd); + ids[1] = chip->read_byte(mtd); + ids[2] = chip->read_byte(mtd); + ids[3] = chip->read_byte(mtd); + chip->select_chip(mtd, -1); + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); +} +#endif + /** * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure --- linux-2.6.22.14/drivers/mtd/nand/rb_ppc.c 1970-01-01 03:00:00.000000000 +0300 +++ linux/drivers/mtd/nand/rb_ppc.c 2008-01-15 11:01:54.000000000 +0200 @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static int common_probe(void); + +static struct mtd_info rmtd; +static struct nand_chip rnand; + +struct rbppc_info { + void *gpi; + void *gpo; + void *localbus; + + unsigned gpio_rdy; + unsigned gpio_nce; + unsigned gpio_cle; + unsigned gpio_ale; + unsigned gpio_ctrls; +}; + +static int rbppc_nand_ready(struct mtd_info *mtd) { + struct nand_chip *chip = mtd->priv; + struct rbppc_info *priv = chip->priv; + + return in_be32(priv->gpi) & priv->gpio_rdy; +} + +static void rbppc_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) { + struct nand_chip *chip = mtd->priv; + struct rbppc_info *priv = chip->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + unsigned val = in_be32(priv->gpo); + if (!(val & priv->gpio_nce)) { + /* make sure Local Bus has done NAND operations */ + readb(priv->localbus); + } + + if (ctrl & NAND_CLE) { + val |= priv->gpio_cle; + } else { + val &= ~priv->gpio_cle; + } + if (ctrl & NAND_ALE) { + val |= priv->gpio_ale; + } else { + val &= ~priv->gpio_ale; + } + if (!(ctrl & NAND_NCE)) { + val |= priv->gpio_nce; + } else { + val &= ~priv->gpio_nce; + } + out_be32(priv->gpo, val); + + /* make sure GPIO output has changed */ + val ^= in_be32(priv->gpo); + if (val & priv->gpio_ctrls) { + printk(KERN_ERR + "NAND GPO change failed 0x%08x\n", val); + } + } + + if (cmd != NAND_CMD_NONE) writeb(cmd, chip->IO_ADDR_W); +} + +static void rbppc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + memcpy(buf, chip->IO_ADDR_R, len); +} + +static int rbppc_nand_probe(struct of_device *pdev, + const struct of_device_id *match) +{ + struct device_node *gpio; + struct device_node *nnand; + struct resource res; + struct rbppc_info *info; + void *baddr; + const unsigned *rdy, *nce, *cle, *ale; + + printk("RB_PPC NAND\n"); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + + rdy = get_property(pdev->node, "rdy", NULL); + nce = get_property(pdev->node, "nce", NULL); + cle = get_property(pdev->node, "cle", NULL); + ale = get_property(pdev->node, "ale", NULL); + + if (!rdy || !nce || !cle || !ale) { + printk("rbppc nand error: gpios properties are missing\n"); + goto err; + } + if (rdy[0] != nce[0] || rdy[0] != cle[0] || rdy[0] != ale[0]) { + printk("rbppc nand error: " + "different gpios are not supported\n"); + goto err; + } + + gpio = of_find_node_by_phandle(rdy[0]); + if (!gpio) { + printk("rbppc nand error: no gpio<%x> node found\n", *rdy); + goto err; + } + if (of_address_to_resource(gpio, 0, &res)) { + printk("rbppc nand error: no reg property in gpio found\n"); + goto err; + } + info->gpo = ioremap_nocache(res.start, res.end - res.start + 1); + + if (!of_address_to_resource(gpio, 1, &res)) { + info->gpi = ioremap_nocache(res.start, res.end - res.start + 1); + } else { + info->gpi = info->gpo; + } + of_node_put(gpio); + + info->gpio_rdy = 1 << (31 - rdy[1]); + info->gpio_nce = 1 << (31 - nce[1]); + info->gpio_cle = 1 << (31 - cle[1]); + info->gpio_ale = 1 << (31 - ale[1]); + info->gpio_ctrls = info->gpio_nce | info->gpio_cle | info->gpio_ale; + + nnand = of_find_node_by_name(NULL, "nnand"); + if (!nnand) { + printk("rbppc nand error: no nnand found\n"); + goto err; + } + if (of_address_to_resource(nnand, 0, &res)) { + printk("rbppc nand error: no reg property in nnand found\n"); + goto err; + } + of_node_put(nnand); + info->localbus = ioremap_nocache(res.start, res.end - res.start + 1); + + if (of_address_to_resource(pdev->node, 0, &res)) { + printk("rbppc nand error: no reg property found\n"); + goto err; + } + baddr = ioremap_nocache(res.start, res.end - res.start + 1); + + memset(&rnand, 0, sizeof(rnand)); + rnand.cmd_ctrl = rbppc_nand_hwcontrol; + rnand.dev_ready = rbppc_nand_ready; + rnand.read_buf = rbppc_read_buf; + rnand.IO_ADDR_W = baddr; + rnand.IO_ADDR_R = baddr; + rnand.priv = info; + + return common_probe(); + + err: + kfree(info); + return -1; +} + +/* common for all NAND chips */ + +static struct mtd_partition partition_info[] = { + { + name: "RouterBoard NAND Boot", + offset: 0, + size: 4 * 1024 * 1024 + }, + { + name: "RouterBoard NAND Main", + offset: MTDPART_OFS_NXTBLK, + size: MTDPART_SIZ_FULL + } +}; + +static unsigned init_ok = 0; + +unsigned get_rbnand_block_size(void) { + if (init_ok) return rmtd.writesize; else return 0; +} + +EXPORT_SYMBOL(get_rbnand_block_size); + +static int common_probe(void) { + memset(&rmtd, 0, sizeof(rmtd)); + + rnand.ecc.mode = NAND_ECC_SOFT; + rnand.chip_delay = 25; + rnand.options |= NAND_NO_AUTOINCR; + rmtd.priv = &rnand; + + if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1) + && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)) { + printk("RBxxx nand device not found\n"); + return -ENXIO; + } + + add_mtd_partitions(&rmtd, partition_info, 2); + init_ok = 1; + return 0; +} + +static struct of_device_id rbppc_nand_ids[] = { + { .name = "nand" }, + {} +}; + +static struct of_platform_driver rbppc_nand_driver = { + .name = "nand", + .probe = rbppc_nand_probe, + .match_table = rbppc_nand_ids, + .driver = { + .name = "rbppc-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init rbppc_nand_init(void) +{ + of_register_platform_driver(&rbppc_nand_driver); + return 0; +} + +static void __exit rbppc_nand_exit(void) +{ + of_unregister_platform_driver(&rbppc_nand_driver); +} + +module_init(rbppc_nand_init); +module_exit(rbppc_nand_exit); --- linux-2.6.22.14/include/asm-powerpc/processor.h 2008-01-22 13:56:30.000000000 +0200 +++ linux/include/asm-powerpc/processor.h 2008-01-15 10:55:55.000000000 +0200 @@ -134,7 +134,7 @@ struct thread_struct { #ifdef CONFIG_PPC32 void *pgdir; /* root of page-table tree */ #endif -#if defined(CONFIG_4xx) || defined (CONFIG_BOOKE) +#if defined(CONFIG_4xx) || defined (CONFIG_BOOKE) || defined(CONFIG_RB_PPC) unsigned long dbcr0; /* debug control register values */ unsigned long dbcr1; #endif --- linux-2.6.22.14/include/linux/mtd/nand.h 2008-01-22 13:56:44.000000000 +0200 +++ linux/include/linux/mtd/nand.h 2008-01-15 10:56:33.000000000 +0200 @@ -399,6 +399,9 @@ struct nand_chip { int subpagesize; uint8_t cellinfo; int badblockpos; +#ifdef MIPSEL + unsigned backup_offset; +#endif nand_state_t state; @@ -421,6 +424,9 @@ struct nand_chip { void *priv; }; +#define BACKUP_4xFF_OFFSET 36 +#define ALWAYS_4x00_OFFSET 32 + /* * NAND Flash Manufacturer ID Codes */