diff options
Diffstat (limited to 'arch')
481 files changed, 36420 insertions, 10324 deletions
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 01b10ab588a..5b7dcd5a0e7 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -67,6 +67,9 @@ config AUTO_IRQ_AFFINITY depends on SMP default y +config ARCH_SUPPORTS_AOUT + def_bool y + source "init/Kconfig" @@ -612,6 +615,11 @@ config VERBOSE_MCHECK_ON Take the default (1) unless you want more control or more info. +config HZ + int + default 1200 if ALPHA_RAWHIDE + default 1024 + source "drivers/pci/Kconfig" source "drivers/eisa/Kconfig" diff --git a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c index e4a0bcf1d28..a872078497b 100644 --- a/arch/alpha/kernel/core_irongate.c +++ b/arch/alpha/kernel/core_irongate.c @@ -241,7 +241,8 @@ albacore_init_arch(void) size / 1024); } #endif - reserve_bootmem_node(NODE_DATA(0), pci_mem, memtop - pci_mem); + reserve_bootmem_node(NODE_DATA(0), pci_mem, memtop - + pci_mem, BOOTMEM_DEFAULT); printk("irongate_init_arch: temporarily reserving " "region %08lx-%08lx for PCI\n", pci_mem, memtop - 1); } diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 72f9a619a66..973c5c3705e 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -22,7 +22,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/utsname.h> #include <linux/time.h> #include <linux/timex.h> diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 92b61629fe3..96ed82fd9ee 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -19,7 +19,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/utsname.h> #include <linux/time.h> #include <linux/major.h> @@ -318,68 +317,6 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, } /* - * Fill in the user structure for an ECOFF core dump. - */ -void -dump_thread(struct pt_regs * pt, struct user * dump) -{ - /* switch stack follows right below pt_regs: */ - struct switch_stack * sw = ((struct switch_stack *) pt) - 1; - - dump->magic = CMAGIC; - dump->start_code = current->mm->start_code; - dump->start_data = current->mm->start_data; - dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); - dump->u_tsize = ((current->mm->end_code - dump->start_code) - >> PAGE_SHIFT); - dump->u_dsize = ((current->mm->brk + PAGE_SIZE-1 - dump->start_data) - >> PAGE_SHIFT); - dump->u_ssize = (current->mm->start_stack - dump->start_stack - + PAGE_SIZE-1) >> PAGE_SHIFT; - - /* - * We store the registers in an order/format that is - * compatible with DEC Unix/OSF/1 as this makes life easier - * for gdb. - */ - dump->regs[EF_V0] = pt->r0; - dump->regs[EF_T0] = pt->r1; - dump->regs[EF_T1] = pt->r2; - dump->regs[EF_T2] = pt->r3; - dump->regs[EF_T3] = pt->r4; - dump->regs[EF_T4] = pt->r5; - dump->regs[EF_T5] = pt->r6; - dump->regs[EF_T6] = pt->r7; - dump->regs[EF_T7] = pt->r8; - dump->regs[EF_S0] = sw->r9; - dump->regs[EF_S1] = sw->r10; - dump->regs[EF_S2] = sw->r11; - dump->regs[EF_S3] = sw->r12; - dump->regs[EF_S4] = sw->r13; - dump->regs[EF_S5] = sw->r14; - dump->regs[EF_S6] = sw->r15; - dump->regs[EF_A3] = pt->r19; - dump->regs[EF_A4] = pt->r20; - dump->regs[EF_A5] = pt->r21; - dump->regs[EF_T8] = pt->r22; - dump->regs[EF_T9] = pt->r23; - dump->regs[EF_T10] = pt->r24; - dump->regs[EF_T11] = pt->r25; - dump->regs[EF_RA] = pt->r26; - dump->regs[EF_T12] = pt->r27; - dump->regs[EF_AT] = pt->r28; - dump->regs[EF_SP] = rdusp(); - dump->regs[EF_PS] = pt->ps; - dump->regs[EF_PC] = pt->pc; - dump->regs[EF_GP] = pt->gp; - dump->regs[EF_A0] = pt->r16; - dump->regs[EF_A1] = pt->r17; - dump->regs[EF_A2] = pt->r18; - memcpy((char *)dump->regs + EF_SIZE, sw->fp, 32 * 8); -} -EXPORT_SYMBOL(dump_thread); - -/* * Fill in the user structure for a ELF core dump. */ void diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index beff6297f78..a449e999027 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -18,7 +18,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/delay.h> #include <linux/mc146818rtc.h> @@ -428,7 +427,8 @@ setup_memory(void *kernel_end) } /* Reserve the bootmap memory. */ - reserve_bootmem(PFN_PHYS(bootmap_start), bootmap_size); + reserve_bootmem(PFN_PHYS(bootmap_start), bootmap_size, + BOOTMEM_DEFAULT); printk("reserving pages %ld:%ld\n", bootmap_start, bootmap_start+PFN_UP(bootmap_size)); #ifdef CONFIG_BLK_DEV_INITRD @@ -446,7 +446,7 @@ setup_memory(void *kernel_end) phys_to_virt(PFN_PHYS(max_low_pfn))); } else { reserve_bootmem(virt_to_phys((void *)initrd_start), - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); } } #endif /* CONFIG_BLK_DEV_INITRD */ @@ -1471,7 +1471,7 @@ c_stop(struct seq_file *f, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index e3e3806a6f2..10ab7833e83 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -242,7 +242,8 @@ setup_memory_node(int nid, void *kernel_end) } /* Reserve the bootmap memory. */ - reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(bootmap_start), bootmap_size); + reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(bootmap_start), + bootmap_size, BOOTMEM_DEFAULT); printk(" reserving pages %ld:%ld\n", bootmap_start, bootmap_start+PFN_UP(bootmap_size)); node_set_online(nid); @@ -281,7 +282,7 @@ setup_memory(void *kernel_end) nid = kvaddr_to_nid(initrd_start); reserve_bootmem_node(NODE_DATA(nid), virt_to_phys((void *)initrd_start), - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); } } #endif /* CONFIG_BLK_DEV_INITRD */ diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e19e7744e36..4127af93c5f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -126,6 +126,9 @@ config GENERIC_CALIBRATE_DELAY bool default y +config ARCH_SUPPORTS_AOUT + def_bool y + config ARCH_MAY_HAVE_PC_FDC bool diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 4f1a03124a7..46bf2ede612 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -18,7 +18,6 @@ #include <linux/unistd.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/delay.h> #include <linux/reboot.h> #include <linux/interrupt.h> @@ -368,35 +367,6 @@ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) EXPORT_SYMBOL(dump_fpu); /* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - struct task_struct *tsk = current; - - dump->magic = CMAGIC; - dump->start_code = tsk->mm->start_code; - dump->start_stack = regs->ARM_sp & ~(PAGE_SIZE - 1); - - dump->u_tsize = (tsk->mm->end_code - tsk->mm->start_code) >> PAGE_SHIFT; - dump->u_dsize = (tsk->mm->brk - tsk->mm->start_data + PAGE_SIZE - 1) >> PAGE_SHIFT; - dump->u_ssize = 0; - - dump->u_debugreg[0] = tsk->thread.debug.bp[0].address; - dump->u_debugreg[1] = tsk->thread.debug.bp[1].address; - dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn.arm; - dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn.arm; - dump->u_debugreg[4] = tsk->thread.debug.nsaved; - - if (dump->start_stack < 0x04000000) - dump->u_ssize = (0x04000000 - dump->start_stack) >> PAGE_SHIFT; - - dump->regs = *regs; - dump->u_fpvalid = dump_fpu (regs, &dump->u_fp); -} -EXPORT_SYMBOL(dump_thread); - -/* * Shuffle the argument into the correct register before calling the * thread function. r1 is the thread argument, r2 is the pointer to * the thread function, and r3 points to the exit function. diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c index 688659668bd..8cb07437a80 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-setup.c +++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c @@ -71,11 +71,11 @@ static struct i2c_board_info __initdata dsmg600_i2c_board_info [] = { static struct gpio_led dsmg600_led_pins[] = { { - .name = "power", + .name = "dsmg600:green:power", .gpio = DSMG600_LED_PWR_GPIO, }, { - .name = "wlan", + .name = "dsmg600:green:wlan", .gpio = DSMG600_LED_WLAN_GPIO, .active_low = true, }, diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index 4cecae84837..159e1c4f1ed 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -60,17 +60,17 @@ static struct i2c_board_info __initdata nas100d_i2c_board_info [] = { static struct gpio_led nas100d_led_pins[] = { { - .name = "wlan", /* green led */ + .name = "nas100d:green:wlan", .gpio = NAS100D_LED_WLAN_GPIO, .active_low = true, }, { - .name = "power", /* blue power led (off=flashing) */ + .name = "nas100d:blue:power", /* (off=flashing) */ .gpio = NAS100D_LED_PWR_GPIO, .active_low = true, }, { - .name = "disk", /* yellow led */ + .name = "nas100d:yellow:disk", .gpio = NAS100D_LED_DISK_GPIO, .active_low = true, }, diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index acaebcbce53..d9a182895a0 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -63,20 +63,20 @@ static struct i2c_board_info __initdata nslu2_i2c_board_info [] = { static struct gpio_led nslu2_led_pins[] = { { - .name = "ready", /* green led */ + .name = "nslu2:green:ready", .gpio = NSLU2_LED_GRN_GPIO, }, { - .name = "status", /* red led */ + .name = "nslu2:red:status", .gpio = NSLU2_LED_RED_GPIO, }, { - .name = "disk-1", + .name = "nslu2:green:disk-1", .gpio = NSLU2_LED_DISK1_GPIO, .active_low = true, }, { - .name = "disk-2", + .name = "nslu2:green:disk-2", .gpio = NSLU2_LED_DISK2_GPIO, .active_low = true, }, diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c index 9b26fa5edad..f99112d50b4 100644 --- a/arch/arm/mach-pxa/tosa.c +++ b/arch/arm/mach-pxa/tosa.c @@ -21,6 +21,8 @@ #include <linux/mmc/host.h> #include <linux/pm.h> #include <linux/delay.h> +#include <linux/gpio_keys.h> +#include <linux/input.h> #include <asm/setup.h> #include <asm/memory.h> @@ -246,6 +248,46 @@ static struct platform_device tosakbd_device = { .id = -1, }; +static struct gpio_keys_button tosa_gpio_keys[] = { + { + .type = EV_PWR, + .code = KEY_SUSPEND, + .gpio = TOSA_GPIO_ON_KEY, + .desc = "On key", + .wakeup = 1, + .active_low = 1, + }, + { + .type = EV_KEY, + .code = TOSA_KEY_RECORD, + .gpio = TOSA_GPIO_RECORD_BTN, + .desc = "Record Button", + .wakeup = 1, + .active_low = 1, + }, + { + .type = EV_KEY, + .code = TOSA_KEY_SYNC, + .gpio = TOSA_GPIO_SYNC, + .desc = "Sync Button", + .wakeup = 1, + .active_low = 1, + }, +}; + +static struct gpio_keys_platform_data tosa_gpio_keys_platform_data = { + .buttons = tosa_gpio_keys, + .nbuttons = ARRAY_SIZE(tosa_gpio_keys), +}; + +static struct platform_device tosa_gpio_keys_device = { + .name = "gpio-keys", + .id = -1, + .dev = { + .platform_data = &tosa_gpio_keys_platform_data, + }, +}; + /* * Tosa LEDs */ @@ -258,6 +300,7 @@ static struct platform_device *devices[] __initdata = { &tosascoop_device, &tosascoop_jc_device, &tosakbd_device, + &tosa_gpio_keys_device, &tosaled_device, }; diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index c0ad7c0fbae..ec00f26bffa 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -239,7 +239,7 @@ bootmem_init_node(int node, int initrd_node, struct meminfo *mi) * Reserve the bootmem bitmap for this node. */ reserve_bootmem_node(pgdat, boot_pfn << PAGE_SHIFT, - boot_pages << PAGE_SHIFT); + boot_pages << PAGE_SHIFT, BOOTMEM_DEFAULT); #ifdef CONFIG_BLK_DEV_INITRD /* @@ -247,7 +247,7 @@ bootmem_init_node(int node, int initrd_node, struct meminfo *mi) */ if (node == initrd_node) { reserve_bootmem_node(pgdat, phys_initrd_start, - phys_initrd_size); + phys_initrd_size, BOOTMEM_DEFAULT); initrd_start = __phys_to_virt(phys_initrd_start); initrd_end = initrd_start + phys_initrd_size; } diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e5d61ee3d4a..d41a75ed3dc 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -605,9 +605,11 @@ void __init reserve_node_zero(pg_data_t *pgdat) * Note that this can only be in node 0. */ #ifdef CONFIG_XIP_KERNEL - reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start); + reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start, + BOOTMEM_DEFAULT); #else - reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); + reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext, + BOOTMEM_DEFAULT); #endif /* @@ -615,7 +617,7 @@ void __init reserve_node_zero(pg_data_t *pgdat) * and can only be in node 0. */ reserve_bootmem_node(pgdat, __pa(swapper_pg_dir), - PTRS_PER_PGD * sizeof(pgd_t)); + PTRS_PER_PGD * sizeof(pgd_t), BOOTMEM_DEFAULT); /* * Hmm... This should go elsewhere, but we really really need to @@ -638,8 +640,10 @@ void __init reserve_node_zero(pg_data_t *pgdat) /* H1940 and RX3715 need to reserve this for suspend */ if (machine_is_h1940() || machine_is_rx3715()) { - reserve_bootmem_node(pgdat, 0x30003000, 0x1000); - reserve_bootmem_node(pgdat, 0x30081000, 0x1000); + reserve_bootmem_node(pgdat, 0x30003000, 0x1000, + BOOTMEM_DEFAULT); + reserve_bootmem_node(pgdat, 0x30081000, 0x1000, + BOOTMEM_DEFAULT); } #ifdef CONFIG_SA1111 @@ -650,7 +654,8 @@ void __init reserve_node_zero(pg_data_t *pgdat) res_size = __pa(swapper_pg_dir) - PHYS_OFFSET; #endif if (res_size) - reserve_bootmem_node(pgdat, PHYS_OFFSET, res_size); + reserve_bootmem_node(pgdat, PHYS_OFFSET, res_size, + BOOTMEM_DEFAULT); } /* diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index 8cd3a60954f..63c62fdea52 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -27,9 +27,11 @@ void __init reserve_node_zero(pg_data_t *pgdat) * Note that this can only be in node 0. */ #ifdef CONFIG_XIP_KERNEL - reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start); + reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start, + BOOTMEM_DEFAULT); #else - reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); + reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext, + BOOTMEM_DEFAULT); #endif /* @@ -37,7 +39,8 @@ void __init reserve_node_zero(pg_data_t *pgdat) * some architectures which the DRAM is the exception vector to trap, * alloc_page breaks with error, although it is not NULL, but "0." */ - reserve_bootmem_node(pgdat, CONFIG_VECTORS_BASE, PAGE_SIZE); + reserve_bootmem_node(pgdat, CONFIG_VECTORS_BASE, PAGE_SIZE, + BOOTMEM_DEFAULT); } /* diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index ee40c1a0b83..7854f19b77c 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -207,7 +207,7 @@ void __init omapfb_reserve_sdram(void) return; } if (rg.paddr) - reserve_bootmem(rg.paddr, rg.size); + reserve_bootmem(rg.paddr, rg.size, BOOTMEM_DEFAULT); reserved += rg.size; omapfb_config.mem_desc.region[i] = rg; configured_regions++; diff --git a/arch/avr32/kernel/ocd.c b/arch/avr32/kernel/ocd.c index c4f023294d7..1b0245d4e0c 100644 --- a/arch/avr32/kernel/ocd.c +++ b/arch/avr32/kernel/ocd.c @@ -90,25 +90,29 @@ static struct dentry *ocd_debugfs_DC; static struct dentry *ocd_debugfs_DS; static struct dentry *ocd_debugfs_count; -static u64 ocd_DC_get(void *data) +static int ocd_DC_get(void *data, u64 *val) { - return ocd_read(DC); + *val = ocd_read(DC); + return 0; } -static void ocd_DC_set(void *data, u64 val) +static int ocd_DC_set(void *data, u64 val) { ocd_write(DC, val); + return 0; } DEFINE_SIMPLE_ATTRIBUTE(fops_DC, ocd_DC_get, ocd_DC_set, "0x%08llx\n"); -static u64 ocd_DS_get(void *data) +static int ocd_DS_get(void *data, u64 *val) { - return ocd_read(DS); + *val = ocd_read(DS); + return 0; } DEFINE_SIMPLE_ATTRIBUTE(fops_DS, ocd_DS_get, NULL, "0x%08llx\n"); -static u64 ocd_count_get(void *data) +static int ocd_count_get(void *data, u64 *val) { - return ocd_count; + *val = ocd_count; + return 0; } DEFINE_SIMPLE_ATTRIBUTE(fops_count, ocd_count_get, NULL, "%lld\n"); diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index 4b4c1884e1c..e66a07a928c 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -489,7 +489,8 @@ static void __init setup_bootmem(void) /* Reserve space for the bootmem bitmap... */ reserve_bootmem_node(NODE_DATA(node), PFN_PHYS(bootmap_pfn), - bootmap_size); + bootmap_size, + BOOTMEM_DEFAULT); /* ...and any other reserved regions. */ for (res = reserved; res; res = res->sibling) { @@ -505,7 +506,8 @@ static void __init setup_bootmem(void) && res->end < PFN_PHYS(max_pfn)) reserve_bootmem_node( NODE_DATA(node), res->start, - res->end - res->start + 1); + res->end - res->start + 1, + BOOTMEM_DEFAULT); } node_set_online(node); diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 14e61f05e1f..7678fee9a88 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1186,6 +1186,59 @@ err_dup_modedb: #endif /* -------------------------------------------------------------------- + * PWM + * -------------------------------------------------------------------- */ +static struct resource atmel_pwm0_resource[] __initdata = { + PBMEM(0xfff01400), + IRQ(24), +}; +static struct clk atmel_pwm0_mck = { + .name = "mck", + .parent = &pbb_clk, + .mode = pbb_clk_mode, + .get_rate = pbb_clk_get_rate, + .index = 5, +}; + +struct platform_device *__init at32_add_device_pwm(u32 mask) +{ + struct platform_device *pdev; + + if (!mask) + return NULL; + + pdev = platform_device_alloc("atmel_pwm", 0); + if (!pdev) + return NULL; + + if (platform_device_add_resources(pdev, atmel_pwm0_resource, + ARRAY_SIZE(atmel_pwm0_resource))) + goto out_free_pdev; + + if (platform_device_add_data(pdev, &mask, sizeof(mask))) + goto out_free_pdev; + + if (mask & (1 << 0)) + select_peripheral(PA(28), PERIPH_A, 0); + if (mask & (1 << 1)) + select_peripheral(PA(29), PERIPH_A, 0); + if (mask & (1 << 2)) + select_peripheral(PA(21), PERIPH_B, 0); + if (mask & (1 << 3)) + select_peripheral(PA(22), PERIPH_B, 0); + + atmel_pwm0_mck.dev = &pdev->dev; + + platform_device_add(pdev); + + return pdev; + +out_free_pdev: + platform_device_put(pdev); + return NULL; +} + +/* -------------------------------------------------------------------- * SSC * -------------------------------------------------------------------- */ static struct resource ssc0_resource[] = { @@ -1646,6 +1699,7 @@ struct clk *at32_clock_list[] = { &atmel_usart1_usart, &atmel_usart2_usart, &atmel_usart3_usart, + &atmel_pwm0_mck, #if defined(CONFIG_CPU_AT32AP7000) &macb0_hclk, &macb0_pclk, diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index ba21e33b8b1..368bc7fe167 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -544,7 +544,7 @@ config EXCPT_IRQ_SYSC_L1 default y help If enabled, the entire ASM lowlevel exception and interrupt entry code - (STORE/RESTORE CONTEXT) is linked into L1 instruction memory. + (STORE/RESTORE CONTEXT) is linked into L1 instruction memory. (less latency) config DO_IRQ_L1 @@ -904,29 +904,38 @@ config ARCH_SUSPEND_POSSIBLE depends on !SMP choice - prompt "Select PM Wakeup Event Source" - default PM_WAKEUP_GPIO_BY_SIC_IWR + prompt "Default Power Saving Mode" depends on PM - help - If you have a GPIO already configured as input with the corresponding PORTx_MASK - bit set - "Specify Wakeup Event by SIC_IWR value" + default PM_BFIN_SLEEP_DEEPER +config PM_BFIN_SLEEP_DEEPER + bool "Sleep Deeper" + help + Sleep "Deeper" Mode (High Power Savings) - This mode reduces dynamic + power dissipation by disabling the clock to the processor core (CCLK). + Furthermore, Standby sets the internal power supply voltage (VDDINT) + to 0.85 V to provide the greatest power savings, while preserving the + processor state. + The PLL and system clock (SCLK) continue to operate at a very low + frequency of about 3.3 MHz. To preserve data integrity in the SDRAM, + the SDRAM is put into Self Refresh Mode. Typically an external event + such as GPIO interrupt or RTC activity wakes up the processor. + Various Peripherals such as UART, SPORT, PPI may not function as + normal during Sleep Deeper, due to the reduced SCLK frequency. + When in the sleep mode, system DMA access to L1 memory is not supported. + +config PM_BFIN_SLEEP + bool "Sleep" + help + Sleep Mode (High Power Savings) - The sleep mode reduces power + dissipation by disabling the clock to the processor core (CCLK). + The PLL and system clock (SCLK), however, continue to operate in + this mode. Typically an external event or RTC activity will wake + up the processor. When in the sleep mode, + system DMA access to L1 memory is not supported. +endchoice -config PM_WAKEUP_GPIO_BY_SIC_IWR - bool "Specify Wakeup Event by SIC_IWR value" config PM_WAKEUP_BY_GPIO bool "Cause Wakeup Event by GPIO" -config PM_WAKEUP_GPIO_API - bool "Configure Wakeup Event by PM GPIO API" - -endchoice - -config PM_WAKEUP_SIC_IWR - hex "Wakeup Events (SIC_IWR)" - depends on PM_WAKEUP_GPIO_BY_SIC_IWR - default 0x8 if (BF537 || BF536 || BF534) - default 0x80 if (BF533 || BF532 || BF531) - default 0x80 if (BF54x) - default 0x80 if (BF52x) config PM_WAKEUP_GPIO_NUMBER int "Wakeup GPIO number" diff --git a/arch/blackfin/Kconfig.debug b/arch/blackfin/Kconfig.debug index 59b87a483c6..c61bdebb997 100644 --- a/arch/blackfin/Kconfig.debug +++ b/arch/blackfin/Kconfig.debug @@ -164,7 +164,7 @@ config DUAL_CORE_TEST_MODULE config CPLB_INFO bool "Display the CPLB information" help - Display the CPLB information. + Display the CPLB information via /proc/cplbinfo. config ACCESS_CHECK bool "Check the user pointer address" diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile index 0edc402fef5..fe254f886a6 100644 --- a/arch/blackfin/Makefile +++ b/arch/blackfin/Makefile @@ -119,6 +119,7 @@ archclean: $(Q)$(MAKE) $(clean)=$(boot) +INSTALL_PATH ?= /tftpboot boot := arch/$(ARCH)/boot BOOT_TARGETS = vmImage PHONY += $(BOOT_TARGETS) install diff --git a/arch/blackfin/configs/BF533-STAMP_defconfig b/arch/blackfin/configs/BF533-STAMP_defconfig index 9b7123cf27a..198f4123af4 100644 --- a/arch/blackfin/configs/BF533-STAMP_defconfig +++ b/arch/blackfin/configs/BF533-STAMP_defconfig @@ -216,8 +216,6 @@ CONFIG_MEM_SIZE=128 CONFIG_MEM_ADD_WIDTH=11 CONFIG_ENET_FLASH_PIN=0 CONFIG_BOOT_LOAD=0x1000 - - CONFIG_BFIN_SCRATCH_REG_RETN=y # CONFIG_BFIN_SCRATCH_REG_RETE is not set # CONFIG_BFIN_SCRATCH_REG_CYCLES is not set @@ -483,7 +481,7 @@ CONFIG_MTD=y # CONFIG_MTD_CONCAT is not set CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set -# CONFIG_MTD_CMDLINE_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y # # User Modules And Translation Layers @@ -500,8 +498,8 @@ CONFIG_MTD_BLOCK=y # # RAM/ROM/Flash chip drivers # -# CONFIG_MTD_CFI is not set -CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_CFI=m +# CONFIG_MTD_JEDECPROBE is not set CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set CONFIG_MTD_MAP_BANK_WIDTH_1=y @@ -515,8 +513,9 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_CFI_I4 is not set # CONFIG_MTD_CFI_I8 is not set # CONFIG_MTD_CFI_INTELEXT is not set -# CONFIG_MTD_CFI_AMDSTD is not set +CONFIG_MTD_CFI_AMDSTD=m # CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=y CONFIG_MTD_ROM=m # CONFIG_MTD_ABSENT is not set @@ -526,6 +525,11 @@ CONFIG_MTD_ROM=m # CONFIG_MTD_COMPLEX_MAPPINGS=y # CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_BF5xx=m +CONFIG_BFIN_FLASH_BANK_0=0x7BB0 +CONFIG_BFIN_FLASH_BANK_1=0x7BB0 +CONFIG_BFIN_FLASH_BANK_2=0x7BB0 +CONFIG_BFIN_FLASH_BANK_3=0x7BB0 # CONFIG_MTD_UCLINUX is not set # CONFIG_MTD_PLATRAM is not set diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index fa9debe8d5f..5453bc3664f 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c @@ -104,6 +104,16 @@ int request_dma(unsigned int channel, char *device_id) mutex_unlock(&(dma_ch[channel].dmalock)); +#ifdef CONFIG_BF54x + if (channel >= CH_UART2_RX && channel <= CH_UART3_TX && + strncmp(device_id, "BFIN_UART", 9) == 0) + dma_ch[channel].regs->peripheral_map |= + (channel - CH_UART2_RX + 0xC); + else + dma_ch[channel].regs->peripheral_map |= + (channel - CH_UART2_RX + 0x6); +#endif + dma_ch[channel].device_id = device_id; dma_ch[channel].irq_callback = NULL; diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index 6bbe0a2fccb..08788f7bbfb 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -186,7 +186,7 @@ static struct str_ident { char name[RESOURCE_LABEL_SIZE]; } str_ident[MAX_RESOURCES]; -#ifdef CONFIG_PM +#if defined(CONFIG_PM) && !defined(CONFIG_BF54x) static unsigned short wakeup_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; static unsigned char wakeup_flags_map[MAX_BLACKFIN_GPIOS]; static struct gpio_port_s gpio_bank_saved[gpio_bank(MAX_BLACKFIN_GPIOS)]; @@ -696,9 +696,8 @@ static int bfin_gpio_wakeup_type(unsigned gpio, unsigned char type) return 0; } -u32 gpio_pm_setup(void) +u32 bfin_pm_setup(void) { - u32 sic_iwr = 0; u16 bank, mask, i, gpio; for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { @@ -723,7 +722,8 @@ u32 gpio_pm_setup(void) gpio = i; while (mask) { - if (mask & 1) { + if ((mask & 1) && (wakeup_flags_map[gpio] != + PM_WAKE_IGNORE)) { reserved_gpio_map[gpio_bank(gpio)] |= gpio_bit(gpio); bfin_gpio_wakeup_type(gpio, @@ -734,21 +734,17 @@ u32 gpio_pm_setup(void) mask >>= 1; } - sic_iwr |= 1 << - (sic_iwr_irqs[bank] - (IRQ_CORETMR + 1)); + bfin_internal_set_wake(sic_iwr_irqs[bank], 1); gpio_bankb[bank]->maskb_set = wakeup_map[gpio_bank(i)]; } } AWA_DUMMY_READ(maskb_set); - if (sic_iwr) - return sic_iwr; - else - return IWR_ENABLE_ALL; + return 0; } -void gpio_pm_restore(void) +void bfin_pm_restore(void) { u16 bank, mask, i; @@ -768,7 +764,7 @@ void gpio_pm_restore(void) reserved_gpio_map[bank] = gpio_bank_saved[bank].reserved; - + bfin_internal_set_wake(sic_iwr_irqs[bank], 0); } gpio_bankb[bank]->maskb = gpio_bank_saved[bank].maskb; diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c index e2e2b5079f5..dc6e8a7a8bd 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbinit.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c @@ -26,6 +26,10 @@ #include <asm/cplb.h> #include <asm/cplbinit.h> +#if ANOMALY_05000263 +# error the MPU will not function safely while Anomaly 05000263 applies +#endif + struct cplb_entry icplb_tbl[MAX_CPLBS]; struct cplb_entry dcplb_tbl[MAX_CPLBS]; diff --git a/arch/blackfin/kernel/init_task.c b/arch/blackfin/kernel/init_task.c index 673c860ffc2..c640154030e 100644 --- a/arch/blackfin/kernel/init_task.c +++ b/arch/blackfin/kernel/init_task.c @@ -57,5 +57,5 @@ EXPORT_SYMBOL(init_task); * "init_task" linker map entry. */ union thread_union init_thread_union - __attribute__ ((__section__(".data.init_task"))) = { + __attribute__ ((__section__(".init_task.data"))) = { INIT_THREAD_INFO(init_task)}; diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 023dc80af18..6b8459c6616 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -31,7 +31,6 @@ #include <linux/smp_lock.h> #include <linux/unistd.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/uaccess.h> #include <linux/fs.h> #include <linux/err.h> diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index 462cae89375..8229b1090eb 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c @@ -1,30 +1,11 @@ /* - * File: arch/blackfin/kernel/setup.c - * Based on: - * Author: + * arch/blackfin/kernel/setup.c * - * Created: - * Description: + * Copyright 2004-2006 Analog Devices Inc. * - * Modified: - * Copyright 2004-2006 Analog Devices Inc. + * Enter bugs at http://blackfin.uclinux.org/ * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. */ #include <linux/delay.h> @@ -34,6 +15,7 @@ #include <linux/cpu.h> #include <linux/module.h> #include <linux/tty.h> +#include <linux/pfn.h> #include <linux/ext2_fs.h> #include <linux/cramfs_fs.h> @@ -47,6 +29,8 @@ #include <asm/fixed_code.h> #include <asm/early_printk.h> +static DEFINE_PER_CPU(struct cpu, cpu_devices); + u16 _bfin_swrst; unsigned long memory_start, memory_end, physical_mem_end; @@ -67,6 +51,29 @@ EXPORT_SYMBOL(mtd_size); char __initdata command_line[COMMAND_LINE_SIZE]; +/* boot memmap, for parsing "memmap=" */ +#define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */ +#define BFIN_MEMMAP_RAM 1 +#define BFIN_MEMMAP_RESERVED 2 +struct bfin_memmap { + int nr_map; + struct bfin_memmap_entry { + unsigned long long addr; /* start of memory segment */ + unsigned long long size; + unsigned long type; + } map[BFIN_MEMMAP_MAX]; +} bfin_memmap __initdata; + +/* for memmap sanitization */ +struct change_member { + struct bfin_memmap_entry *pentry; /* pointer to original entry */ + unsigned long long addr; /* address for this change point */ +}; +static struct change_member change_point_list[2*BFIN_MEMMAP_MAX] __initdata; +static struct change_member *change_point[2*BFIN_MEMMAP_MAX] __initdata; +static struct bfin_memmap_entry *overlap_list[BFIN_MEMMAP_MAX] __initdata; +static struct bfin_memmap_entry new_map[BFIN_MEMMAP_MAX] __initdata; + void __init bf53x_cache_init(void) { #if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE) @@ -123,12 +130,224 @@ void __init bf53x_relocate_l1_mem(void) } +/* add_memory_region to memmap */ +static void __init add_memory_region(unsigned long long start, + unsigned long long size, int type) +{ + int i; + + i = bfin_memmap.nr_map; + + if (i == BFIN_MEMMAP_MAX) { + printk(KERN_ERR "Ooops! Too many entries in the memory map!\n"); + return; + } + + bfin_memmap.map[i].addr = start; + bfin_memmap.map[i].size = size; + bfin_memmap.map[i].type = type; + bfin_memmap.nr_map++; +} + +/* + * Sanitize the boot memmap, removing overlaps. + */ +static int __init sanitize_memmap(struct bfin_memmap_entry *map, int *pnr_map) +{ + struct change_member *change_tmp; + unsigned long current_type, last_type; + unsigned long long last_addr; + int chgidx, still_changing; + int overlap_entries; + int new_entry; + int old_nr, new_nr, chg_nr; + int i; + + /* + Visually we're performing the following (1,2,3,4 = memory types) + + Sample memory map (w/overlaps): + ____22__________________ + ______________________4_ + ____1111________________ + _44_____________________ + 11111111________________ + ____________________33__ + ___________44___________ + __________33333_________ + ______________22________ + ___________________2222_ + _________111111111______ + _____________________11_ + _________________4______ + + Sanitized equivalent (no overlap): + 1_______________________ + _44_____________________ + ___1____________________ + ____22__________________ + ______11________________ + _________1______________ + __________3_____________ + ___________44___________ + _____________33_________ + _______________2________ + ________________1_______ + _________________4______ + ___________________2____ + ____________________33__ + ______________________4_ + */ + /* if there's only one memory region, don't bother */ + if (*pnr_map < 2) + return -1; + + old_nr = *pnr_map; + + /* bail out if we find any unreasonable addresses in memmap */ + for (i = 0; i < old_nr; i++) + if (map[i].addr + map[i].size < map[i].addr) + return -1; + + /* create pointers for initial change-point information (for sorting) */ + for (i = 0; i < 2*old_nr; i++) + change_point[i] = &change_point_list[i]; + + /* record all known change-points (starting and ending addresses), + omitting those that are for empty memory regions */ + chgidx = 0; + for (i = 0; i < old_nr; i++) { + if (map[i].size != 0) { + change_point[chgidx]->addr = map[i].addr; + change_point[chgidx++]->pentry = &map[i]; + change_point[chgidx]->addr = map[i].addr + map[i].size; + change_point[chgidx++]->pentry = &map[i]; + } + } + chg_nr = chgidx; /* true number of change-points */ + + /* sort change-point list by memory addresses (low -> high) */ + still_changing = 1; + while (still_changing) { + still_changing = 0; + for (i = 1; i < chg_nr; i++) { + /* if <current_addr> > <last_addr>, swap */ + /* or, if current=<start_addr> & last=<end_addr>, swap */ + if ((change_point[i]->addr < change_point[i-1]->addr) || + ((change_point[i]->addr == change_point[i-1]->addr) && + (change_point[i]->addr == change_point[i]->pentry->addr) && + (change_point[i-1]->addr != change_point[i-1]->pentry->addr)) + ) { + change_tmp = change_point[i]; + change_point[i] = change_point[i-1]; + change_point[i-1] = change_tmp; + still_changing = 1; + } + } + } + + /* create a new memmap, removing overlaps */ + overlap_entries = 0; /* number of entries in the overlap table */ + new_entry = 0; /* index for creating new memmap entries */ + last_type = 0; /* start with undefined memory type */ + last_addr = 0; /* start with 0 as last starting address */ + /* loop through change-points, determining affect on the new memmap */ + for (chgidx = 0; chgidx < chg_nr; chgidx++) { + /* keep track of all overlapping memmap entries */ + if (change_point[chgidx]->addr == change_point[chgidx]->pentry->addr) { + /* add map entry to overlap list (> 1 entry implies an overlap) */ + overlap_list[overlap_entries++] = change_point[chgidx]->pentry; + } else { + /* remove entry from list (order independent, so swap with last) */ + for (i = 0; i < overlap_entries; i++) { + if (overlap_list[i] == change_point[chgidx]->pentry) + overlap_list[i] = overlap_list[overlap_entries-1]; + } + overlap_entries--; + } + /* if there are overlapping entries, decide which "type" to use */ + /* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */ + current_type = 0; + for (i = 0; i < overlap_entries; i++) + if (overlap_list[i]->type > current_type) + current_type = overlap_list[i]->type; + /* continue building up new memmap based on this information */ + if (current_type != last_type) { + if (last_type != 0) { + new_map[new_entry].size = + change_point[chgidx]->addr - last_addr; + /* move forward only if the new size was non-zero */ + if (new_map[new_entry].size != 0) + if (++new_entry >= BFIN_MEMMAP_MAX) + break; /* no more space left for new entries */ + } + if (current_type != 0) { + new_map[new_entry].addr = change_point[chgidx]->addr; + new_map[new_entry].type = current_type; + last_addr = change_point[chgidx]->addr; + } + last_type = current_type; + } + } + new_nr = new_entry; /* retain count for new entries */ + + /* copy new mapping into original location */ + memcpy(map, new_map, new_nr*sizeof(struct bfin_memmap_entry)); + *pnr_map = new_nr; + + return 0; +} + +static void __init print_memory_map(char *who) +{ + int i; + + for (i = 0; i < bfin_memmap.nr_map; i++) { + printk(KERN_DEBUG " %s: %016Lx - %016Lx ", who, + bfin_memmap.map[i].addr, + bfin_memmap.map[i].addr + bfin_memmap.map[i].size); + switch (bfin_memmap.map[i].type) { + case BFIN_MEMMAP_RAM: + printk("(usable)\n"); + break; + case BFIN_MEMMAP_RESERVED: + printk("(reserved)\n"); + break; + default: printk("type %lu\n", bfin_memmap.map[i].type); + break; + } + } +} + +static __init int parse_memmap(char *arg) +{ + unsigned long long start_at, mem_size; + + if (!arg) + return -EINVAL; + + mem_size = memparse(arg, &arg); + if (*arg == '@') { + start_at = memparse(arg+1, &arg); + add_memory_region(start_at, mem_size, BFIN_MEMMAP_RAM); + } else if (*arg == '$') { + start_at = memparse(arg+1, &arg); + add_memory_region(start_at, mem_size, BFIN_MEMMAP_RESERVED); + } + + return 0; +} + /* * Initial parsing of the command line. Currently, we support: * - Controlling the linux memory size: mem=xxx[KMG] * - Controlling the physical memory size: max_mem=xxx[KMG][$][#] * $ -> reserved memory is dcacheable * # -> reserved memory is icacheable + * - "memmap=XXX[KkmM][@][$]XXX[KkmM]" defines a memory region + * @ from <start> to <start>+<mem>, type RAM + * $ from <start> to <start>+<mem>, type RESERVED + * */ static __init void parse_cmdline_early(char *cmdline_p) { @@ -136,7 +355,6 @@ static __init void parse_cmdline_early(char *cmdline_p) unsigned int memsize; for (;;) { if (c == ' ') { - if (!memcmp(to, "mem=", 4)) { to += 4; memsize = memparse(to, &to); @@ -162,6 +380,9 @@ static __init void parse_cmdline_early(char *cmdline_p) } else if (!memcmp(to, "earlyprintk=", 12)) { to += 12; setup_early_printk(to); + } else if (!memcmp(to, "memmap=", 7)) { + to += 7; + parse_memmap(to); } } c = *(to++); @@ -170,75 +391,36 @@ static __init void parse_cmdline_early(char *cmdline_p) } } -void __init setup_arch(char **cmdline_p) +/* + * Setup memory defaults from user config. + * The physical memory layout looks like: + * + * [_rambase, _ramstart]: kernel image + * [memory_start, memory_end]: dynamic memory managed by kernel + * [memory_end, _ramend]: reserved memory + * [meory_mtd_start(memory_end), + * memory_mtd_start + mtd_size]: rootfs (if any) + * [_ramend - DMA_UNCACHED_REGION, + * _ramend]: uncached DMA region + * [_ramend, physical_mem_end]: memory not managed by kernel + * + */ +static __init void memory_setup(void) { - int bootmap_size; - unsigned long l1_length, sclk, cclk; #ifdef CONFIG_MTD_UCLINUX unsigned long mtd_phys = 0; #endif -#ifdef CONFIG_DUMMY_CONSOLE - conswitchp = &dummy_con; -#endif - -#if defined(CONFIG_CMDLINE_BOOL) - strncpy(&command_line[0], CONFIG_CMDLINE, sizeof(command_line)); - command_line[sizeof(command_line) - 1] = 0; -#endif - - /* Keep a copy of command line */ - *cmdline_p = &command_line[0]; - memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); - boot_command_line[COMMAND_LINE_SIZE - 1] = '\0'; - - /* setup memory defaults from the user config */ - physical_mem_end = 0; - _ramend = CONFIG_MEM_SIZE * 1024 * 1024; - - parse_cmdline_early(&command_line[0]); - - cclk = get_cclk(); - sclk = get_sclk(); - -#if !defined(CONFIG_BFIN_KERNEL_CLOCK) - if (ANOMALY_05000273 && cclk == sclk) - panic("ANOMALY 05000273, SCLK can not be same as CCLK"); -#endif + _rambase = (unsigned long)_stext; + _ramstart = (unsigned long)_end; -#ifdef BF561_FAMILY - if (ANOMALY_05000266) { - bfin_read_IMDMA_D0_IRQ_STATUS(); - bfin_read_IMDMA_D1_IRQ_STATUS(); + if (DMA_UNCACHED_REGION > (_ramend - _ramstart)) { + console_init(); + panic("DMA region exceeds memory limit: %lu.\n", + _ramend - _ramstart); } -#endif - - printk(KERN_INFO "Hardware Trace "); - if (bfin_read_TBUFCTL() & 0x1 ) - printk("Active "); - else - printk("Off "); - if (bfin_read_TBUFCTL() & 0x2) - printk("and Enabled\n"); - else - printk("and Disabled\n"); - - -#if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH) - /* we need to initialize the Flashrom device here since we might - * do things with flash early on in the boot - */ - flash_probe(); -#endif - - if (physical_mem_end == 0) - physical_mem_end = _ramend; - - /* by now the stack is part of the init task */ memory_end = _ramend - DMA_UNCACHED_REGION; - _ramstart = (unsigned long)__bss_stop; - _rambase = (unsigned long)_stext; #ifdef CONFIG_MPU /* Round up to multiple of 4MB. */ memory_start = (_ramstart + 0x3fffff) & ~0x3fffff; @@ -292,7 +474,7 @@ void __init setup_arch(char **cmdline_p) } /* Relocate MTD image to the top of memory after the uncached memory area */ - dma_memcpy((char *)memory_end, __bss_stop, mtd_size); + dma_memcpy((char *)memory_end, _end, mtd_size); memory_mtd_start = memory_end; _ebss = memory_mtd_start; /* define _ebss for compatible */ @@ -319,13 +501,175 @@ void __init setup_arch(char **cmdline_p) #endif #if !defined(CONFIG_MTD_UCLINUX) - memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ + /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ + memory_end -= SIZE_4K; #endif + init_mm.start_code = (unsigned long)_stext; init_mm.end_code = (unsigned long)_etext; init_mm.end_data = (unsigned long)_edata; init_mm.brk = (unsigned long)0; + printk(KERN_INFO "Board Memory: %ldMB\n", physical_mem_end >> 20); + printk(KERN_INFO "Kernel Managed Memory: %ldMB\n", _ramend >> 20); + + printk(KERN_INFO "Memory map:\n" + KERN_INFO " text = 0x%p-0x%p\n" + KERN_INFO " rodata = 0x%p-0x%p\n" + KERN_INFO " bss = 0x%p-0x%p\n" + KERN_INFO " data = 0x%p-0x%p\n" + KERN_INFO " stack = 0x%p-0x%p\n" + KERN_INFO " init = 0x%p-0x%p\n" + KERN_INFO " available = 0x%p-0x%p\n" +#ifdef CONFIG_MTD_UCLINUX + KERN_INFO " rootfs = 0x%p-0x%p\n" +#endif +#if DMA_UNCACHED_REGION > 0 + KERN_INFO " DMA Zone = 0x%p-0x%p\n" +#endif + , _stext, _etext, + __start_rodata, __end_rodata, + __bss_start, __bss_stop, + _sdata, _edata, + (void *)&init_thread_union, + (void *)((int)(&init_thread_union) + 0x2000), + __init_begin, __init_end, + (void *)_ramstart, (void *)memory_end +#ifdef CONFIG_MTD_UCLINUX + , (void *)memory_mtd_start, (void *)(memory_mtd_start + mtd_size) +#endif +#if DMA_UNCACHED_REGION > 0 + , (void *)(_ramend - DMA_UNCACHED_REGION), (void *)(_ramend) +#endif + ); +} + +static __init void setup_bootmem_allocator(void) +{ + int bootmap_size; + int i; + unsigned long min_pfn, max_pfn; + unsigned long curr_pfn, last_pfn, size; + + /* mark memory between memory_start and memory_end usable */ + add_memory_region(memory_start, + memory_end - memory_start, BFIN_MEMMAP_RAM); + /* sanity check for overlap */ + sanitize_memmap(bfin_memmap.map, &bfin_memmap.nr_map); + print_memory_map("boot memmap"); + + min_pfn = PAGE_OFFSET >> PAGE_SHIFT; + max_pfn = memory_end >> PAGE_SHIFT; + + /* + * give all the memory to the bootmap allocator, tell it to put the + * boot mem_map at the start of memory. + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), + memory_start >> PAGE_SHIFT, /* map goes here */ + min_pfn, max_pfn); + + /* register the memmap regions with the bootmem allocator */ + for (i = 0; i < bfin_memmap.nr_map; i++) { + /* + * Reserve usable memory + */ + if (bfin_memmap.map[i].type != BFIN_MEMMAP_RAM) + continue; + /* + * We are rounding up the start address of usable memory: + */ + curr_pfn = PFN_UP(bfin_memmap.map[i].addr); + if (curr_pfn >= max_pfn) + continue; + /* + * ... and at the end of the usable range downwards: + */ + last_pfn = PFN_DOWN(bfin_memmap.map[i].addr + + bfin_memmap.map[i].size); + + if (last_pfn > max_pfn) + last_pfn = max_pfn; + + /* + * .. finally, did all the rounding and playing + * around just make the area go away? + */ + if (last_pfn <= curr_pfn) + continue; + + size = last_pfn - curr_pfn; + free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size)); + } + + /* reserve memory before memory_start, including bootmap */ + reserve_bootmem(PAGE_OFFSET, + memory_start + bootmap_size + PAGE_SIZE - 1 - PAGE_OFFSET, + BOOTMEM_DEFAULT); +} + +void __init setup_arch(char **cmdline_p) +{ + unsigned long l1_length, sclk, cclk; + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + +#if defined(CONFIG_CMDLINE_BOOL) + strncpy(&command_line[0], CONFIG_CMDLINE, sizeof(command_line)); + command_line[sizeof(command_line) - 1] = 0; +#endif + + /* Keep a copy of command line */ + *cmdline_p = &command_line[0]; + memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); + boot_command_line[COMMAND_LINE_SIZE - 1] = '\0'; + + /* setup memory defaults from the user config */ + physical_mem_end = 0; + _ramend = CONFIG_MEM_SIZE * 1024 * 1024; + + memset(&bfin_memmap, 0, sizeof(bfin_memmap)); + + parse_cmdline_early(&command_line[0]); + + if (physical_mem_end == 0) + physical_mem_end = _ramend; + + memory_setup(); + + cclk = get_cclk(); + sclk = get_sclk(); + +#if !defined(CONFIG_BFIN_KERNEL_CLOCK) + if (ANOMALY_05000273 && cclk == sclk) + panic("ANOMALY 05000273, SCLK can not be same as CCLK"); +#endif + +#ifdef BF561_FAMILY + if (ANOMALY_05000266) { + bfin_read_IMDMA_D0_IRQ_STATUS(); + bfin_read_IMDMA_D1_IRQ_STATUS(); + } +#endif + printk(KERN_INFO "Hardware Trace "); + if (bfin_read_TBUFCTL() & 0x1) + printk("Active "); + else + printk("Off "); + if (bfin_read_TBUFCTL() & 0x2) + printk("and Enabled\n"); + else + printk("and Disabled\n"); + +#if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH) + /* we need to initialize the Flashrom device here since we might + * do things with flash early on in the boot + */ + flash_probe(); +#endif + _bfin_swrst = bfin_read_SWRST(); if (_bfin_swrst & RESET_DOUBLE) @@ -335,7 +679,7 @@ void __init setup_arch(char **cmdline_p) else if (_bfin_swrst & RESET_SOFTWARE) printk(KERN_NOTICE "Reset caused by Software reset\n"); - printk(KERN_INFO "Blackfin support (C) 2004-2007 Analog Devices, Inc.\n"); + printk(KERN_INFO "Blackfin support (C) 2004-2008 Analog Devices, Inc.\n"); if (bfin_compiled_revid() == 0xffff) printk(KERN_INFO "Compiled for ADSP-%s Rev any\n", CPU); else if (bfin_compiled_revid() == -1) @@ -361,55 +705,8 @@ void __init setup_arch(char **cmdline_p) if (ANOMALY_05000273 && (cclk >> 1) <= sclk) printk("\n\n\nANOMALY_05000273: CCLK must be >= 2*SCLK !!!\n\n\n"); - printk(KERN_INFO "Board Memory: %ldMB\n", physical_mem_end >> 20); - printk(KERN_INFO "Kernel Managed Memory: %ldMB\n", _ramend >> 20); - - printk(KERN_INFO "Memory map:\n" - KERN_INFO " text = 0x%p-0x%p\n" - KERN_INFO " rodata = 0x%p-0x%p\n" - KERN_INFO " data = 0x%p-0x%p\n" - KERN_INFO " stack = 0x%p-0x%p\n" - KERN_INFO " init = 0x%p-0x%p\n" - KERN_INFO " bss = 0x%p-0x%p\n" - KERN_INFO " available = 0x%p-0x%p\n" -#ifdef CONFIG_MTD_UCLINUX - KERN_INFO " rootfs = 0x%p-0x%p\n" -#endif -#if DMA_UNCACHED_REGION > 0 - KERN_INFO " DMA Zone = 0x%p-0x%p\n" -#endif - , _stext, _etext, - __start_rodata, __end_rodata, - _sdata, _edata, - (void *)&init_thread_union, (void *)((int)(&init_thread_union) + 0x2000), - __init_begin, __init_end, - __bss_start, __bss_stop, - (void *)_ramstart, (void *)memory_end -#ifdef CONFIG_MTD_UCLINUX - , (void *)memory_mtd_start, (void *)(memory_mtd_start + mtd_size) -#endif -#if DMA_UNCACHED_REGION > 0 - , (void *)(_ramend - DMA_UNCACHED_REGION), (void *)(_ramend) -#endif - ); + setup_bootmem_allocator(); - /* - * give all the memory to the bootmap allocator, tell it to put the - * boot mem_map at the start of memory - */ - bootmap_size = init_bootmem_node(NODE_DATA(0), memory_start >> PAGE_SHIFT, /* map goes here */ - PAGE_OFFSET >> PAGE_SHIFT, - memory_end >> PAGE_SHIFT); - /* - * free the usable memory, we have to make sure we do not free - * the bootmem bitmap so we then reserve it after freeing it :-) - */ - free_bootmem(memory_start, memory_end - memory_start); - - reserve_bootmem(memory_start, bootmap_size); - /* - * get kmalloc into gear - */ paging_init(); /* check the size of the l1 area */ @@ -450,15 +747,15 @@ void __init setup_arch(char **cmdline_p) static int __init topology_init(void) { -#if defined (CONFIG_BF561) - static struct cpu cpu[2]; - register_cpu(&cpu[0], 0); - register_cpu(&cpu[1], 1); + int cpu; + + for_each_possible_cpu(cpu) { + struct cpu *c = &per_cpu(cpu_devices, cpu); + + register_cpu(c, cpu); + } + return 0; -#else - static struct cpu cpu[1]; - return register_cpu(cpu, 0); -#endif } subsys_initcall(topology_init); @@ -700,7 +997,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 66b5f3e3ae2..58717cb1970 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -649,7 +649,7 @@ void dump_bfin_process(struct pt_regs *fp) if (context & 0x0020 && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) printk(KERN_NOTICE "HW Error context\n"); else if (context & 0x0020) - printk(KERN_NOTICE "Defered Exception context\n"); + printk(KERN_NOTICE "Deferred Exception context\n"); else if (context & 0x3FC0) printk(KERN_NOTICE "Interrupt context\n"); else if (context & 0x4000) diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S index 858722421b4..aed832540b3 100644 --- a/arch/blackfin/kernel/vmlinux.lds.S +++ b/arch/blackfin/kernel/vmlinux.lds.S @@ -41,6 +41,9 @@ _jiffies = _jiffies_64; SECTIONS { . = CONFIG_BOOT_LOAD; + /* Neither the text, ro_data or bss section need to be aligned + * So pack them back to back + */ .text : { __text = .; @@ -58,22 +61,25 @@ SECTIONS *(__ex_table) ___stop___ex_table = .; - . = ALIGN(4); __etext = .; } - RO_DATA(PAGE_SIZE) + /* Just in case the first read only is a 32-bit access */ + RO_DATA(4) + + .bss : + { + . = ALIGN(4); + ___bss_start = .; + *(.bss .bss.*) + *(COMMON) + ___bss_stop = .; + } .data : { - /* make sure the init_task is aligned to the - * kernel thread size so we can locate the kernel - * stack properly and quickly. - */ __sdata = .; - . = ALIGN(THREAD_SIZE); - *(.data.init_task) - + /* This gets done first, so the glob doesn't suck it in */ . = ALIGN(32); *(.data.cacheline_aligned) @@ -81,10 +87,22 @@ SECTIONS *(.data.*) CONSTRUCTORS + /* make sure the init_task is aligned to the + * kernel thread size so we can locate the kernel + * stack properly and quickly. + */ . = ALIGN(THREAD_SIZE); + *(.init_task.data) + __edata = .; } + /* The init section should be last, so when we free it, it goes into + * the general memory pool, and (hopefully) will decrease fragmentation + * a tiny bit. The init section has a _requirement_ that it be + * PAGE_SIZE aligned + */ + . = ALIGN(PAGE_SIZE); ___init_begin = .; .init.text : @@ -179,16 +197,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); ___init_end = .; - .bss : - { - . = ALIGN(4); - ___bss_start = .; - *(.bss .bss.*) - *(COMMON) - . = ALIGN(4); - ___bss_stop = .; - __end = .; - } + __end =.; STABS_DEBUG diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c index 1795aab7906..337515fba61 100644 --- a/arch/blackfin/mach-bf527/boards/ezkit.c +++ b/arch/blackfin/mach-bf527/boards/ezkit.c @@ -41,7 +41,9 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/usb/sl811.h> +#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> +#endif #include <asm/cplb.h> #include <asm/dma.h> #include <asm/bfin5xx_spi.h> @@ -517,6 +519,14 @@ static struct bfin5xx_spi_chip spi_wm8731_chip_info = { .bits_per_word = 16, }; #endif + +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ || defined(CONFIG_MTD_M25P80_MODULE) @@ -634,6 +644,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .mode = SPI_MODE_0, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI controller data */ diff --git a/arch/blackfin/mach-bf533/boards/ezkit.c b/arch/blackfin/mach-bf533/boards/ezkit.c index 4026c2f3ab4..2b09aa39f56 100644 --- a/arch/blackfin/mach-bf533/boards/ezkit.c +++ b/arch/blackfin/mach-bf533/boards/ezkit.c @@ -34,7 +34,9 @@ #include <linux/mtd/partitions.h> #include <linux/spi/spi.h> #include <linux/spi/flash.h> +#if defined(CONFIG_USB_ISP1362_HCD) || defined(CONFIG_USB_ISP1362_HCD_MODULE) #include <linux/usb/isp1362.h> +#endif #include <linux/ata_platform.h> #include <linux/irq.h> #include <asm/dma.h> @@ -134,6 +136,13 @@ static struct bfin5xx_spi_chip ad1836_spi_chip_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) { @@ -168,6 +177,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &ad1836_spi_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI (0) */ diff --git a/arch/blackfin/mach-bf533/boards/stamp.c b/arch/blackfin/mach-bf533/boards/stamp.c index 0185350feac..a645f6fd091 100644 --- a/arch/blackfin/mach-bf533/boards/stamp.c +++ b/arch/blackfin/mach-bf533/boards/stamp.c @@ -226,6 +226,13 @@ static struct bfin5xx_spi_chip spi_mmc_chip_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) { @@ -312,6 +319,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .mode = SPI_MODE_2, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI (0) */ @@ -423,9 +439,9 @@ static struct platform_device bfin_pata_device = { #include <linux/gpio_keys.h> static struct gpio_keys_button bfin_gpio_keys_table[] = { - {BTN_0, GPIO_PF5, 1, "gpio-keys: BTN0"}, - {BTN_1, GPIO_PF6, 1, "gpio-keys: BTN1"}, - {BTN_2, GPIO_PF8, 1, "gpio-keys: BTN2"}, + {BTN_0, GPIO_PF5, 0, "gpio-keys: BTN0"}, + {BTN_1, GPIO_PF6, 0, "gpio-keys: BTN1"}, + {BTN_2, GPIO_PF8, 0, "gpio-keys: BTN2"}, }; static struct gpio_keys_platform_data bfin_gpio_keys_data = { diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c index 119e6ea8338..9e2277e0d25 100644 --- a/arch/blackfin/mach-bf537/boards/stamp.c +++ b/arch/blackfin/mach-bf537/boards/stamp.c @@ -487,6 +487,13 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bfin_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ || defined(CONFIG_MTD_M25P80_MODULE) @@ -593,6 +600,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &spi_ad7877_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI controller data */ diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c index 14860f04d1b..916e963e83b 100644 --- a/arch/blackfin/mach-bf548/boards/ezkit.c +++ b/arch/blackfin/mach-bf548/boards/ezkit.c @@ -37,7 +37,9 @@ #include <linux/spi/flash.h> #include <linux/irq.h> #include <linux/interrupt.h> +#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> +#endif #include <asm/bfin5xx_spi.h> #include <asm/cplb.h> #include <asm/dma.h> @@ -420,6 +422,13 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { }; #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + static struct spi_board_info bf54x_spi_board_info[] __initdata = { #if defined(CONFIG_MTD_M25P80) \ || defined(CONFIG_MTD_M25P80_MODULE) @@ -445,6 +454,15 @@ static struct spi_board_info bf54x_spi_board_info[] __initdata = { .controller_data = &spi_ad7877_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; /* SPI (0) */ @@ -631,7 +649,7 @@ static struct platform_device *ezkit_devices[] __initdata = { &ezkit_flash_device, }; -static int __init stamp_init(void) +static int __init ezkit_init(void) { printk(KERN_INFO "%s(): registering device resources\n", __FUNCTION__); platform_add_devices(ezkit_devices, ARRAY_SIZE(ezkit_devices)); @@ -644,4 +662,4 @@ static int __init stamp_init(void) return 0; } -arch_initcall(stamp_init); +arch_initcall(ezkit_init); diff --git a/arch/blackfin/mach-bf548/dma.c b/arch/blackfin/mach-bf548/dma.c index 957bf1366ef..374803a8d2e 100644 --- a/arch/blackfin/mach-bf548/dma.c +++ b/arch/blackfin/mach-bf548/dma.c @@ -1,5 +1,5 @@ /* - * File: arch/blackfin/mach-bf561/dma.c + * File: arch/blackfin/mach-bf548/dma.c * Based on: * Author: * @@ -7,7 +7,7 @@ * Description: This file contains the simple DMA Implementation for Blackfin * * Modified: - * Copyright 2004-2007 Analog Devices Inc. + * Copyright 2004-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c index ed863ce9a2d..43c1b098281 100644 --- a/arch/blackfin/mach-bf561/boards/ezkit.c +++ b/arch/blackfin/mach-bf561/boards/ezkit.c @@ -92,6 +92,68 @@ void __exit bfin_isp1761_exit(void) arch_initcall(bfin_isp1761_init); #endif +#if defined(CONFIG_USB_ISP1362_HCD) || defined(CONFIG_USB_ISP1362_HCD_MODULE) +#include <linux/usb/isp1362.h> + +static struct resource isp1362_hcd_resources[] = { + { + .start = 0x2c060000, + .end = 0x2c060000, + .flags = IORESOURCE_MEM, + }, { + .start = 0x2c060004, + .end = 0x2c060004, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_PF8, + .end = IRQ_PF8, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct isp1362_platform_data isp1362_priv = { + .sel15Kres = 1, + .clknotstop = 0, + .oc_enable = 0, + .int_act_high = 0, + .int_edge_triggered = 0, + .remote_wakeup_connected = 0, + .no_power_switching = 1, + .power_switching_mode = 0, +}; + +static struct platform_device isp1362_hcd_device = { + .name = "isp1362-hcd", + .id = 0, + .dev = { + .platform_data = &isp1362_priv, + }, + .num_resources = ARRAY_SIZE(isp1362_hcd_resources), + .resource = isp1362_hcd_resources, +}; +#endif + +#if defined(CONFIG_USB_NET2272) || defined(CONFIG_USB_NET2272_MODULE) +static struct resource net2272_bfin_resources[] = { + { + .start = 0x2C000000, + .end = 0x2C000000 + 0x7F, + .flags = IORESOURCE_MEM, + }, { + .start = IRQ_PF10, + .end = IRQ_PF10, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL, + }, +}; + +static struct platform_device net2272_bfin_device = { + .name = "net2272", + .id = -1, + .num_resources = ARRAY_SIZE(net2272_bfin_resources), + .resource = net2272_bfin_resources, +}; +#endif + /* * USB-LAN EzExtender board * Driver needs to know address, irq and flag pin. @@ -204,6 +266,13 @@ static struct bfin5xx_spi_chip ad1836_spi_chip_info = { .bits_per_word = 16, }; #endif + +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif #endif /* SPI (0) */ @@ -248,6 +317,15 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &ad1836_spi_chip_info, }, #endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif }; #if defined(CONFIG_PATA_PLATFORM) || defined(CONFIG_PATA_PLATFORM_MODULE) @@ -340,6 +418,10 @@ static struct platform_device *ezkit_devices[] __initdata = { &ax88180_device, #endif +#if defined(CONFIG_USB_NET2272) || defined(CONFIG_USB_NET2272_MODULE) + &net2272_bfin_device, +#endif + #if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE) &bfin_spi0_device, #endif @@ -359,6 +441,11 @@ static struct platform_device *ezkit_devices[] __initdata = { #if defined(CONFIG_I2C_GPIO) || defined(CONFIG_I2C_GPIO_MODULE) &i2c_gpio_device, #endif + +#if defined(CONFIG_USB_ISP1362_HCD) || defined(CONFIG_USB_ISP1362_HCD_MODULE) + &isp1362_hcd_device, +#endif + &ezkit_flash_device, }; diff --git a/arch/blackfin/mach-common/Makefile b/arch/blackfin/mach-common/Makefile index 8636d4284bd..15e33ca1ce8 100644 --- a/arch/blackfin/mach-common/Makefile +++ b/arch/blackfin/mach-common/Makefile @@ -4,8 +4,6 @@ obj-y := \ cache.o cacheinit.o entry.o \ - interrupt.o lock.o irqpanic.o arch_checks.o + interrupt.o lock.o irqpanic.o arch_checks.o ints-priority.o -obj-$(CONFIG_BFIN_SINGLE_CORE) += ints-priority-sc.o -obj-$(CONFIG_BFIN_DUAL_CORE) += ints-priority-dc.o obj-$(CONFIG_PM) += pm.o dpmc.o diff --git a/arch/blackfin/mach-common/dpmc.S b/arch/blackfin/mach-common/dpmc.S index b82c096e198..b80ddd8b232 100644 --- a/arch/blackfin/mach-common/dpmc.S +++ b/arch/blackfin/mach-common/dpmc.S @@ -191,6 +191,9 @@ ENTRY(_sleep_mode) call _test_pll_locked; R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; P0.H = hi(PLL_CTL); @@ -237,6 +240,10 @@ ENTRY(_deep_sleep) CLI R4; + R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; call _set_dram_srfs; @@ -261,6 +268,9 @@ ENTRY(_deep_sleep) call _test_pll_locked; R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; P0.H = hi(PLL_CTL); @@ -286,7 +296,13 @@ ENTRY(_sleep_deeper) CLI R4; P3 = R0; + P4 = R1; + P5 = R2; + R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; call _set_dram_srfs; /* Set SDRAM Self Refresh */ @@ -327,6 +343,8 @@ ENTRY(_sleep_deeper) call _test_pll_locked; R0 = P3; + R1 = P4; + R3 = P5; call _set_sic_iwr; /* Set Awake from IDLE */ P0.H = hi(PLL_CTL); @@ -340,6 +358,9 @@ ENTRY(_sleep_deeper) call _test_pll_locked; R0 = IWR_ENABLE(0); + R1 = IWR_DISABLE_ALL; + R2 = IWR_DISABLE_ALL; + call _set_sic_iwr; /* Set Awake from IDLE PLL */ P0.H = hi(VR_CTL); @@ -417,14 +438,23 @@ ENTRY(_unset_dram_srfs) RTS; ENTRY(_set_sic_iwr) -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) P0.H = hi(SIC_IWR0); P0.L = lo(SIC_IWR0); + P1.H = hi(SIC_IWR1); + P1.L = lo(SIC_IWR1); + [P1] = R1; +#if defined(CONFIG_BF54x) + P1.H = hi(SIC_IWR2); + P1.L = lo(SIC_IWR2); + [P1] = R2; +#endif #else P0.H = hi(SIC_IWR); P0.L = lo(SIC_IWR); #endif [P0] = R0; + SSYNC; RTS; diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S index fdd9bf43361..2cbb7a0bc38 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S @@ -121,6 +121,7 @@ ENTRY(_ex_icplb_miss) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; SAVE_ALL_SYS + DEBUG_HWTRACE_SAVE(p5, r7) #ifdef CONFIG_MPU R0 = SEQSTAT; R1 = SP; @@ -132,14 +133,13 @@ ENTRY(_ex_icplb_miss) #else call __cplb_hdr; #endif - DEBUG_START_HWTRACE(p5, r7) + DEBUG_HWTRACE_RESTORE(p5, r7) RESTORE_ALL_SYS SP = EX_SCRATCH_REG; rtx; ENDPROC(_ex_icplb_miss) ENTRY(_ex_syscall) - DEBUG_START_HWTRACE(p5, r7) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; raise 15; /* invoked by TRAP #0, for sys call */ @@ -178,7 +178,6 @@ ENTRY(_ex_single_step) ENDPROC(_ex_single_step) ENTRY(_bfin_return_from_exception) - DEBUG_START_HWTRACE(p5, r7) #if ANOMALY_05000257 R7=LC0; LC0=R7; @@ -200,10 +199,9 @@ ENTRY(_handle_bad_cplb) * need to make a CPLB exception look like a normal exception */ - DEBUG_START_HWTRACE(p5, r7) RESTORE_ALL_SYS [--sp] = ASTAT; - [--sp] = (R7:6, P5:4); + [--sp] = (R7:6,P5:4); ENTRY(_ex_replaceable) nop; @@ -253,7 +251,6 @@ ENTRY(_ex_trap_c) R6 = SEQSTAT; [P5] = R6; - DEBUG_START_HWTRACE(p5, r7) (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; SP = EX_SCRATCH_REG; @@ -382,8 +379,7 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ sp.h = _exception_stack_top; /* Try to deal with syscalls quickly. */ [--sp] = ASTAT; - [--sp] = (R7:6, P5:4); - DEBUG_STOP_HWTRACE(p5, r7) + [--sp] = (R7:6,P5:4); r7 = SEQSTAT; /* reason code is in bit 5:0 */ r6.l = lo(SEQSTAT_EXCAUSE); r6.h = hi(SEQSTAT_EXCAUSE); diff --git a/arch/blackfin/mach-common/ints-priority-dc.c b/arch/blackfin/mach-common/ints-priority-dc.c deleted file mode 100644 index 8d18d6b163b..00000000000 --- a/arch/blackfin/mach-common/ints-priority-dc.c +++ /dev/null @@ -1,484 +0,0 @@ -/* - * File: arch/blackfin/mach-common/ints-priority-dc.c - * Based on: - * Author: - * - * Created: ? - * Description: Set up the interrupt priorities - * - * Modified: - * 1996 Roman Zippel - * 1999 D. Jeff Dionne <jeff@uclinux.org> - * 2000-2001 Lineo, Inc. D. Jefff Dionne <jeff@lineo.ca> - * 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> - * 2003 Metrowerks/Motorola - * 2003 Bas Vermeulen <bas@buyways.nl> - * Copyright 2004-2006 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <linux/module.h> -#include <linux/kernel_stat.h> -#include <linux/seq_file.h> -#include <linux/irq.h> -#ifdef CONFIG_KGDB -#include <linux/kgdb.h> -#endif -#include <asm/traps.h> -#include <asm/blackfin.h> -#include <asm/gpio.h> -#include <asm/irq_handler.h> - -/* - * NOTES: - * - we have separated the physical Hardware interrupt from the - * levels that the LINUX kernel sees (see the description in irq.h) - * - - */ - -/* Initialize this to an actual value to force it into the .data - * section so that we know it is properly initialized at entry into - * the kernel but before bss is initialized to zero (which is where - * it would live otherwise). The 0x1f magic represents the IRQs we - * cannot actually mask out in hardware. - */ -unsigned long irq_flags = 0x1f; - -/* The number of spurious interrupts */ -atomic_t num_spurious; - -struct ivgx { - /* irq number for request_irq, available in mach-bf561/irq.h */ - int irqno; - /* corresponding bit in the SICA_ISR0 register */ - int isrflag0; - /* corresponding bit in the SICA_ISR1 register */ - int isrflag1; -} ivg_table[NR_PERI_INTS]; - -struct ivg_slice { - /* position of first irq in ivg_table for given ivg */ - struct ivgx *ifirst; - struct ivgx *istop; -} ivg7_13[IVG13 - IVG7 + 1]; - -static void search_IAR(void); - -/* - * Search SIC_IAR and fill tables with the irqvalues - * and their positions in the SIC_ISR register. - */ -static void __init search_IAR(void) -{ - unsigned ivg, irq_pos = 0; - for (ivg = 0; ivg <= IVG13 - IVG7; ivg++) { - int irqn; - - ivg7_13[ivg].istop = ivg7_13[ivg].ifirst = &ivg_table[irq_pos]; - - for (irqn = 0; irqn < NR_PERI_INTS; irqn++) { - int iar_shift = (irqn & 7) * 4; - if (ivg == - (0xf & - bfin_read32((unsigned long *)SICA_IAR0 + - (irqn >> 3)) >> iar_shift)) { - ivg_table[irq_pos].irqno = IVG7 + irqn; - ivg_table[irq_pos].isrflag0 = - (irqn < 32 ? (1 << irqn) : 0); - ivg_table[irq_pos].isrflag1 = - (irqn < 32 ? 0 : (1 << (irqn - 32))); - ivg7_13[ivg].istop++; - irq_pos++; - } - } - } -} - -/* - * This is for BF561 internal IRQs - */ - -static void ack_noop(unsigned int irq) -{ - /* Dummy function. */ -} - -static void bf561_core_mask_irq(unsigned int irq) -{ - irq_flags &= ~(1 << irq); - if (!irqs_disabled()) - local_irq_enable(); -} - -static void bf561_core_unmask_irq(unsigned int irq) -{ - irq_flags |= 1 << irq; - /* - * If interrupts are enabled, IMASK must contain the same value - * as irq_flags. Make sure that invariant holds. If interrupts - * are currently disabled we need not do anything; one of the - * callers will take care of setting IMASK to the proper value - * when reenabling interrupts. - * local_irq_enable just does "STI irq_flags", so it's exactly - * what we need. - */ - if (!irqs_disabled()) - local_irq_enable(); - return; -} - -static void bf561_internal_mask_irq(unsigned int irq) -{ - unsigned long irq_mask; - if ((irq - (IRQ_CORETMR + 1)) < 32) { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); - bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() & ~irq_mask); - } else { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); - bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() & ~irq_mask); - } -} - -static void bf561_internal_unmask_irq(unsigned int irq) -{ - unsigned long irq_mask; - - if ((irq - (IRQ_CORETMR + 1)) < 32) { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1))); - bfin_write_SICA_IMASK0(bfin_read_SICA_IMASK0() | irq_mask); - } else { - irq_mask = (1 << (irq - (IRQ_CORETMR + 1) - 32)); - bfin_write_SICA_IMASK1(bfin_read_SICA_IMASK1() | irq_mask); - } - SSYNC(); -} - -static struct irq_chip bf561_core_irqchip = { - .ack = ack_noop, - .mask = bf561_core_mask_irq, - .unmask = bf561_core_unmask_irq, -}; - -static struct irq_chip bf561_internal_irqchip = { - .ack = ack_noop, - .mask = bf561_internal_mask_irq, - .unmask = bf561_internal_unmask_irq, -}; - -static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; -static unsigned short gpio_edge_triggered[gpio_bank(MAX_BLACKFIN_GPIOS)]; - -static void bf561_gpio_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } -} - -static void bf561_gpio_mask_ack_irq(unsigned int irq) -{ - u16 gpionr = irq - IRQ_PF0; - - if (gpio_edge_triggered[gpio_bank(gpionr)] & gpio_bit(gpionr)) { - set_gpio_data(gpionr, 0); - SSYNC(); - } - - set_gpio_maska(gpionr, 0); - SSYNC(); -} - -static void bf561_gpio_mask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 0); - SSYNC(); -} - -static void bf561_gpio_unmask_irq(unsigned int irq) -{ - set_gpio_maska(irq - IRQ_PF0, 1); - SSYNC(); -} - -static unsigned int bf561_gpio_irq_startup(unsigned int irq) -{ - unsigned int ret; - char buf[8]; - u16 gpionr = irq - IRQ_PF0; - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { - snprintf(buf, sizeof buf, "IRQ %d", irq); - ret = gpio_request(gpionr, buf); - if (ret) - return ret; - - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - bf561_gpio_unmask_irq(irq); - - return ret; - -} - -static void bf561_gpio_irq_shutdown(unsigned int irq) -{ - bf561_gpio_mask_irq(irq); - gpio_free(irq - IRQ_PF0); - gpio_enabled[gpio_bank(irq - IRQ_PF0)] &= ~gpio_bit(irq - IRQ_PF0); -} - -static int bf561_gpio_irq_type(unsigned int irq, unsigned int type) -{ - - unsigned int ret; - char buf[8]; - u16 gpionr = irq - IRQ_PF0; - - - if (type == IRQ_TYPE_PROBE) { - /* only probe unenabled GPIO interrupt lines */ - if (gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr)) - return 0; - type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; - - } - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | - IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { - - if (!(gpio_enabled[gpio_bank(gpionr)] & gpio_bit(gpionr))) { - snprintf(buf, sizeof buf, "IRQ %d", irq); - ret = gpio_request(gpionr, buf); - if (ret) - return ret; - - } - - gpio_enabled[gpio_bank(gpionr)] |= gpio_bit(gpionr); - } else { - gpio_enabled[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - return 0; - } - - - set_gpio_dir(gpionr, 0); - set_gpio_inen(gpionr, 1); - - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); - set_gpio_edge(gpionr, 1); - } else { - set_gpio_edge(gpionr, 0); - gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - } - - if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - set_gpio_both(gpionr, 1); - else - set_gpio_both(gpionr, 0); - - if ((type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW))) - set_gpio_polar(gpionr, 1); /* low or falling edge denoted by one */ - else - set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */ - - SSYNC(); - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - set_irq_handler(irq, handle_edge_irq); - else - set_irq_handler(irq, handle_level_irq); - - return 0; -} - -static struct irq_chip bf561_gpio_irqchip = { - .ack = bf561_gpio_ack_irq, - .mask = bf561_gpio_mask_irq, - .mask_ack = bf561_gpio_mask_ack_irq, - .unmask = bf561_gpio_unmask_irq, - .set_type = bf561_gpio_irq_type, - .startup = bf561_gpio_irq_startup, - .shutdown = bf561_gpio_irq_shutdown -}; - -static void bf561_demux_gpio_irq(unsigned int inta_irq, - struct irq_desc *intb_desc) -{ - int irq, flag_d, mask; - u16 gpio; - - switch (inta_irq) { - case IRQ_PROG0_INTA: - irq = IRQ_PF0; - break; - case IRQ_PROG1_INTA: - irq = IRQ_PF16; - break; - case IRQ_PROG2_INTA: - irq = IRQ_PF32; - break; - default: - dump_stack(); - return; - } - - gpio = irq - IRQ_PF0; - - flag_d = get_gpiop_data(gpio); - mask = flag_d & (gpio_enabled[gpio_bank(gpio)] & - get_gpiop_maska(gpio)); - - do { - if (mask & 1) { - struct irq_desc *desc = irq_desc + irq; - desc->handle_irq(irq, desc); - } - irq++; - mask >>= 1; - } while (mask); - - -} - -void __init init_exception_vectors(void) -{ - SSYNC(); - - /* cannot program in software: - * evt0 - emulation (jtag) - * evt1 - reset - */ - bfin_write_EVT2(evt_nmi); - bfin_write_EVT3(trap); - bfin_write_EVT5(evt_ivhw); - bfin_write_EVT6(evt_timer); - bfin_write_EVT7(evt_evt7); - bfin_write_EVT8(evt_evt8); - bfin_write_EVT9(evt_evt9); - bfin_write_EVT10(evt_evt10); - bfin_write_EVT11(evt_evt11); - bfin_write_EVT12(evt_evt12); - bfin_write_EVT13(evt_evt13); - bfin_write_EVT14(evt14_softirq); - bfin_write_EVT15(evt_system_call); - CSYNC(); -} - -/* - * This function should be called during kernel startup to initialize - * the BFin IRQ handling routines. - */ -int __init init_arch_irq(void) -{ - int irq; - unsigned long ilat = 0; - /* Disable all the peripheral intrs - page 4-29 HW Ref manual */ - bfin_write_SICA_IMASK0(SIC_UNMASK_ALL); - bfin_write_SICA_IMASK1(SIC_UNMASK_ALL); - SSYNC(); - - bfin_write_SICA_IWR0(IWR_ENABLE_ALL); - bfin_write_SICA_IWR1(IWR_ENABLE_ALL); - - local_irq_disable(); - - init_exception_buff(); - - for (irq = 0; irq <= SYS_IRQS; irq++) { - if (irq <= IRQ_CORETMR) - set_irq_chip(irq, &bf561_core_irqchip); - else - set_irq_chip(irq, &bf561_internal_irqchip); - - if ((irq != IRQ_PROG0_INTA) && - (irq != IRQ_PROG1_INTA) && - (irq != IRQ_PROG2_INTA)) - set_irq_handler(irq, handle_simple_irq); - else - set_irq_chained_handler(irq, bf561_demux_gpio_irq); - } - - for (irq = IRQ_PF0; irq <= IRQ_PF47; irq++) { - set_irq_chip(irq, &bf561_gpio_irqchip); - /* if configured as edge, then will be changed to do_edge_IRQ */ - set_irq_handler(irq, handle_level_irq); - } - - bfin_write_IMASK(0); - CSYNC(); - ilat = bfin_read_ILAT(); - CSYNC(); - bfin_write_ILAT(ilat); - CSYNC(); - - printk(KERN_INFO "Configuring Blackfin Priority Driven Interrupts\n"); - /* IMASK=xxx is equivalent to STI xx or irq_flags=xx, - * local_irq_enable() - */ - program_IAR(); - /* Therefore it's better to setup IARs before interrupts enabled */ - search_IAR(); - - /* Enable interrupts IVG7-15 */ - irq_flags = irq_flags | IMASK_IVG15 | - IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | - IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; - - return 0; -} - -#ifdef CONFIG_DO_IRQ_L1 -__attribute__((l1_text)) -#endif -void do_irq(int vec, struct pt_regs *fp) -{ - if (vec == EVT_IVTMR_P) { - vec = IRQ_CORETMR; - } else { - struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; - struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; - unsigned long sic_status0, sic_status1; - - SSYNC(); - sic_status0 = bfin_read_SICA_IMASK0() & bfin_read_SICA_ISR0(); - sic_status1 = bfin_read_SICA_IMASK1() & bfin_read_SICA_ISR1(); - - for (;; ivg++) { - if (ivg >= ivg_stop) { - atomic_inc(&num_spurious); - return; - } else if ((sic_status0 & ivg->isrflag0) || - (sic_status1 & ivg->isrflag1)) - break; - } - vec = ivg->irqno; - } - asm_do_IRQ(vec, fp); - -#ifdef CONFIG_KGDB - kgdb_process_breakpoint(); -#endif -} diff --git a/arch/blackfin/mach-common/ints-priority-sc.c b/arch/blackfin/mach-common/ints-priority.c index dec42acb5de..880595afe98 100644 --- a/arch/blackfin/mach-common/ints-priority-sc.c +++ b/arch/blackfin/mach-common/ints-priority.c @@ -1,5 +1,5 @@ /* - * File: arch/blackfin/mach-common/ints-priority-sc.c + * File: arch/blackfin/mach-common/ints-priority.c * Based on: * Author: * @@ -13,7 +13,7 @@ * 2002 Arcturus Networks Inc. MaTed <mated@sympatico.ca> * 2003 Metrowerks/Motorola * 2003 Bas Vermeulen <bas@buyways.nl> - * Copyright 2004-2007 Analog Devices Inc. + * Copyright 2004-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -69,6 +69,10 @@ unsigned long irq_flags = 0x1f; /* The number of spurious interrupts */ atomic_t num_spurious; +#ifdef CONFIG_PM +unsigned long bfin_sic_iwr[3]; /* Up to 3 SIC_IWRx registers */ +#endif + struct ivgx { /* irq number for request_irq, available in mach-bf533/irq.h */ unsigned int irqno; @@ -98,8 +102,7 @@ static void __init search_IAR(void) for (irqn = 0; irqn < NR_PERI_INTS; irqn++) { int iar_shift = (irqn & 7) * 4; - if (ivg == - (0xf & + if (ivg == (0xf & #ifndef CONFIG_BF52x bfin_read32((unsigned long *)SIC_IAR0 + (irqn >> 3)) >> iar_shift)) { @@ -179,6 +182,27 @@ static void bfin_internal_unmask_irq(unsigned int irq) SSYNC(); } +#ifdef CONFIG_PM +int bfin_internal_set_wake(unsigned int irq, unsigned int state) +{ + unsigned bank, bit; + unsigned long flags; + bank = (irq - (IRQ_CORETMR + 1)) / 32; + bit = (irq - (IRQ_CORETMR + 1)) % 32; + + local_irq_save(flags); + + if (state) + bfin_sic_iwr[bank] |= (1 << bit); + else + bfin_sic_iwr[bank] &= ~(1 << bit); + + local_irq_restore(flags); + + return 0; +} +#endif + static struct irq_chip bfin_core_irqchip = { .ack = ack_noop, .mask = bfin_core_mask_irq, @@ -189,6 +213,9 @@ static struct irq_chip bfin_internal_irqchip = { .ack = ack_noop, .mask = bfin_internal_mask_irq, .unmask = bfin_internal_unmask_irq, +#ifdef CONFIG_PM + .set_wake = bfin_internal_set_wake, +#endif }; #ifdef BF537_GENERIC_ERROR_INT_DEMUX @@ -206,8 +233,7 @@ static void bfin_generic_error_mask_irq(unsigned int irq) if (!error_int_mask) { local_irq_disable(); bfin_write_SIC_IMASK(bfin_read_SIC_IMASK() & - ~(1 << - (IRQ_GENERIC_ERROR - + ~(1 << (IRQ_GENERIC_ERROR - (IRQ_CORETMR + 1)))); SSYNC(); local_irq_enable(); @@ -232,7 +258,7 @@ static struct irq_chip bfin_generic_error_irqchip = { }; static void bfin_demux_error_irq(unsigned int int_err_irq, - struct irq_desc *intb_desc) + struct irq_desc *inta_desc) { int irq = 0; @@ -404,16 +430,8 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) return 0; } + set_gpio_inen(gpionr, 0); set_gpio_dir(gpionr, 0); - set_gpio_inen(gpionr, 1); - - if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { - gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); - set_gpio_edge(gpionr, 1); - } else { - set_gpio_edge(gpionr, 0); - gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); - } if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) == (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) @@ -426,6 +444,18 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) else set_gpio_polar(gpionr, 0); /* high or rising edge denoted by zero */ + if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { + set_gpio_edge(gpionr, 1); + set_gpio_inen(gpionr, 1); + gpio_edge_triggered[gpio_bank(gpionr)] |= gpio_bit(gpionr); + set_gpio_data(gpionr, 0); + + } else { + set_gpio_edge(gpionr, 0); + gpio_edge_triggered[gpio_bank(gpionr)] &= ~gpio_bit(gpionr); + set_gpio_inen(gpionr, 1); + } + SSYNC(); if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) @@ -436,6 +466,20 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) return 0; } +#ifdef CONFIG_PM +int bfin_gpio_set_wake(unsigned int irq, unsigned int state) +{ + unsigned gpio = irq_to_gpio(irq); + + if (state) + gpio_pm_wakeup_request(gpio, PM_WAKE_IGNORE); + else + gpio_pm_wakeup_free(gpio); + + return 0; +} +#endif + static struct irq_chip bfin_gpio_irqchip = { .ack = bfin_gpio_ack_irq, .mask = bfin_gpio_mask_irq, @@ -443,30 +487,87 @@ static struct irq_chip bfin_gpio_irqchip = { .unmask = bfin_gpio_unmask_irq, .set_type = bfin_gpio_irq_type, .startup = bfin_gpio_irq_startup, - .shutdown = bfin_gpio_irq_shutdown + .shutdown = bfin_gpio_irq_shutdown, +#ifdef CONFIG_PM + .set_wake = bfin_gpio_set_wake, +#endif }; -static void bfin_demux_gpio_irq(unsigned int intb_irq, - struct irq_desc *intb_desc) +static void bfin_demux_gpio_irq(unsigned int inta_irq, + struct irq_desc *desc) { - u16 i; - struct irq_desc *desc; + unsigned int i, gpio, mask, irq, search = 0; - for (i = 0; i < MAX_BLACKFIN_GPIOS; i += 16) { - int irq = IRQ_PF0 + i; - int flag_d = get_gpiop_data(i); - int mask = - flag_d & (gpio_enabled[gpio_bank(i)] & get_gpiop_maska(i)); + switch (inta_irq) { +#if defined(CONFIG_BF53x) + case IRQ_PROG_INTA: + irq = IRQ_PF0; + search = 1; + break; +# if defined(BF537_FAMILY) && !(defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) + case IRQ_MAC_RX: + irq = IRQ_PH0; + break; +# endif +#elif defined(CONFIG_BF52x) + case IRQ_PORTF_INTA: + irq = IRQ_PF0; + break; + case IRQ_PORTG_INTA: + irq = IRQ_PG0; + break; + case IRQ_PORTH_INTA: + irq = IRQ_PH0; + break; +#elif defined(CONFIG_BF561) + case IRQ_PROG0_INTA: + irq = IRQ_PF0; + break; + case IRQ_PROG1_INTA: + irq = IRQ_PF16; + break; + case IRQ_PROG2_INTA: + irq = IRQ_PF32; + break; +#endif + default: + BUG(); + return; + } - while (mask) { - if (mask & 1) { - desc = irq_desc + irq; - desc->handle_irq(irq, desc); + if (search) { + for (i = 0; i < MAX_BLACKFIN_GPIOS; i += GPIO_BANKSIZE) { + irq += i; + + mask = get_gpiop_data(i) & + (gpio_enabled[gpio_bank(i)] & + get_gpiop_maska(i)); + + while (mask) { + if (mask & 1) { + desc = irq_desc + irq; + desc->handle_irq(irq, desc); + } + irq++; + mask >>= 1; } - irq++; - mask >>= 1; } + } else { + gpio = irq_to_gpio(irq); + mask = get_gpiop_data(gpio) & + (gpio_enabled[gpio_bank(gpio)] & + get_gpiop_maska(gpio)); + + do { + if (mask & 1) { + desc = irq_desc + irq; + desc->handle_irq(irq, desc); + } + irq++; + mask >>= 1; + } while (mask); } + } #else /* CONFIG_BF54x */ @@ -711,6 +812,74 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) return 0; } +#ifdef CONFIG_PM +u32 pint_saved_masks[NR_PINT_SYS_IRQS]; +u32 pint_wakeup_masks[NR_PINT_SYS_IRQS]; + +int bfin_gpio_set_wake(unsigned int irq, unsigned int state) +{ + u32 pint_irq; + u8 pint_val = irq2pint_lut[irq - SYS_IRQS]; + u32 bank = PINT_2_BANK(pint_val); + u32 pintbit = PINT_BIT(pint_val); + + switch (bank) { + case 0: + pint_irq = IRQ_PINT0; + break; + case 2: + pint_irq = IRQ_PINT2; + break; + case 3: + pint_irq = IRQ_PINT3; + break; + case 1: + pint_irq = IRQ_PINT1; + break; + default: + return -EINVAL; + } + + bfin_internal_set_wake(pint_irq, state); + + if (state) + pint_wakeup_masks[bank] |= pintbit; + else + pint_wakeup_masks[bank] &= ~pintbit; + + return 0; +} + +u32 bfin_pm_setup(void) +{ + u32 val, i; + + for (i = 0; i < NR_PINT_SYS_IRQS; i++) { + val = pint[i]->mask_clear; + pint_saved_masks[i] = val; + if (val ^ pint_wakeup_masks[i]) { + pint[i]->mask_clear = val; + pint[i]->mask_set = pint_wakeup_masks[i]; + } + } + + return 0; +} + +void bfin_pm_restore(void) +{ + u32 i, val; + + for (i = 0; i < NR_PINT_SYS_IRQS; i++) { + val = pint_saved_masks[i]; + if (val ^ pint_wakeup_masks[i]) { + pint[i]->mask_clear = pint[i]->mask_clear; + pint[i]->mask_set = val; + } + } +} +#endif + static struct irq_chip bfin_gpio_irqchip = { .ack = bfin_gpio_ack_irq, .mask = bfin_gpio_mask_irq, @@ -718,17 +887,19 @@ static struct irq_chip bfin_gpio_irqchip = { .unmask = bfin_gpio_unmask_irq, .set_type = bfin_gpio_irq_type, .startup = bfin_gpio_irq_startup, - .shutdown = bfin_gpio_irq_shutdown + .shutdown = bfin_gpio_irq_shutdown, +#ifdef CONFIG_PM + .set_wake = bfin_gpio_set_wake, +#endif }; -static void bfin_demux_gpio_irq(unsigned int intb_irq, - struct irq_desc *intb_desc) +static void bfin_demux_gpio_irq(unsigned int inta_irq, + struct irq_desc *desc) { u8 bank, pint_val; u32 request, irq; - struct irq_desc *desc; - switch (intb_irq) { + switch (inta_irq) { case IRQ_PINT0: bank = 0; break; @@ -795,7 +966,7 @@ int __init init_arch_irq(void) int irq; unsigned long ilat = 0; /* Disable all the peripheral intrs - page 4-29 HW Ref manual */ -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) bfin_write_SIC_IMASK0(SIC_UNMASK_ALL); bfin_write_SIC_IMASK1(SIC_UNMASK_ALL); bfin_write_SIC_IWR0(IWR_ENABLE_ALL); @@ -812,6 +983,8 @@ int __init init_arch_irq(void) local_irq_disable(); + init_exception_buff(); + #ifdef CONFIG_BF54x # ifdef CONFIG_PINTx_REASSIGN pint[0]->assign = CONFIG_PINT0_ASSIGN; @@ -874,6 +1047,19 @@ int __init init_arch_irq(void) set_irq_chained_handler(irq, bfin_demux_gpio_irq); break; +#elif defined(CONFIG_BF561) + case IRQ_PROG0_INTA: + set_irq_chained_handler(irq, + bfin_demux_gpio_irq); + break; + case IRQ_PROG1_INTA: + set_irq_chained_handler(irq, + bfin_demux_gpio_irq); + break; + case IRQ_PROG2_INTA: + set_irq_chained_handler(irq, + bfin_demux_gpio_irq); + break; #endif default: set_irq_handler(irq, handle_simple_irq); @@ -893,11 +1079,8 @@ int __init init_arch_irq(void) } #endif -#ifndef CONFIG_BF54x - for (irq = IRQ_PF0; irq < NR_IRQS; irq++) { -#else - for (irq = IRQ_PA0; irq < NR_IRQS; irq++) { -#endif + for (irq = GPIO_IRQ_BASE; irq < NR_IRQS; irq++) { + set_irq_chip(irq, &bfin_gpio_irqchip); /* if configured as edge, then will be changed to do_edge_IRQ */ set_irq_handler(irq, handle_level_irq); @@ -936,7 +1119,7 @@ void do_irq(int vec, struct pt_regs *fp) } else { struct ivgx *ivg = ivg7_13[vec - IVG7].ifirst; struct ivgx *ivg_stop = ivg7_13[vec - IVG7].istop; -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) unsigned long sic_status[3]; SSYNC(); diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c index 81930f7d06f..0be805ca423 100644 --- a/arch/blackfin/mach-common/pm.c +++ b/arch/blackfin/mach-common/pm.c @@ -4,7 +4,7 @@ * Author: Cliff Brake <cbrake@accelent.com> Copyright (c) 2001 * * Created: 2001 - * Description: Power management for the bfin + * Description: Blackfin power management * * Modified: Nicolas Pitre - PXA250 support * Copyright (c) 2002 Monta Vista Software, Inc. @@ -12,7 +12,7 @@ * Copyright (c) 2002 Monta Vista Software, Inc. * Dirk Behme <dirk.behme@de.bosch.com> - OMAP1510/1610 * Copyright 2004 - * Copyright 2004-2006 Analog Devices Inc. + * Copyright 2004-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -67,42 +67,30 @@ void bfin_pm_suspend_standby_enter(void) gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); #endif -#if defined(CONFIG_PM_WAKEUP_BY_GPIO) || defined(CONFIG_PM_WAKEUP_GPIO_API) - { - u32 flags; + u32 flags; - local_irq_save(flags); + local_irq_save(flags); + bfin_pm_setup(); - sleep_deeper(gpio_pm_setup()); /*Goto Sleep*/ - - gpio_pm_restore(); - -#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) - bfin_write_SIC_IWR0(IWR_ENABLE_ALL); - bfin_write_SIC_IWR1(IWR_ENABLE_ALL); -# ifdef CONFIG_BF54x - bfin_write_SIC_IWR2(IWR_ENABLE_ALL); -# endif +#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER + sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); #else - bfin_write_SIC_IWR(IWR_ENABLE_ALL); + sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); #endif - local_irq_restore(flags); - } -#endif + bfin_pm_restore(); -#if defined(CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR) - sleep_deeper(CONFIG_PM_WAKEUP_SIC_IWR); -# if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) +#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) bfin_write_SIC_IWR0(IWR_ENABLE_ALL); bfin_write_SIC_IWR1(IWR_ENABLE_ALL); -# ifdef CONFIG_BF54x +# ifdef CONFIG_BF54x bfin_write_SIC_IWR2(IWR_ENABLE_ALL); -# endif -# else - bfin_write_SIC_IWR(IWR_ENABLE_ALL); # endif -#endif /* CONFIG_PM_WAKEUP_GPIO_BY_SIC_IWR */ +#else + bfin_write_SIC_IWR(IWR_ENABLE_ALL); +#endif + + local_irq_restore(flags); } /* diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index eb1a12ac9e3..1f516c55bde 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c @@ -138,8 +138,7 @@ void __init mem_init(void) start_mem = PAGE_ALIGN(start_mem); max_mapnr = num_physpages = MAP_NR(high_memory); - printk(KERN_INFO "Kernel managed physical pages: %lu\n", - num_physpages); + printk(KERN_DEBUG "Kernel managed physical pages: %lu\n", num_physpages); /* This will put all memory onto the freelists. */ totalram_pages = free_all_bootmem(); @@ -153,8 +152,7 @@ void __init mem_init(void) /* do not count in kernel image between _rambase and _ramstart */ reservedpages -= (_ramstart - _rambase) >> PAGE_SHIFT; #if (defined(CONFIG_BFIN_ICACHE) && ANOMALY_05000263) - reservedpages += (_ramend - memory_end - DMA_UNCACHED_REGION) >> - PAGE_SHIFT; + reservedpages += (_ramend - memory_end - DMA_UNCACHED_REGION) >> PAGE_SHIFT; #endif codek = (_etext - _stext) >> 10; @@ -163,11 +161,9 @@ void __init mem_init(void) printk(KERN_INFO "Memory available: %luk/%luk RAM, " - "(%uk init code, %uk kernel code, " - "%uk data, %uk dma, %uk reserved)\n", + "(%uk init code, %uk kernel code, %uk data, %uk dma, %uk reserved)\n", (unsigned long) freepages << (PAGE_SHIFT-10), _ramend >> 10, - initk, codek, datak, DMA_UNCACHED_REGION >> 10, - (reservedpages << (PAGE_SHIFT-10))); + initk, codek, datak, DMA_UNCACHED_REGION >> 10, (reservedpages << (PAGE_SHIFT-10))); /* Initialize the blackfin L1 Memory. */ l1sram_init(); diff --git a/arch/cris/Kconfig b/arch/cris/Kconfig index 27b082ac7f1..8456bc8efb7 100644 --- a/arch/cris/Kconfig +++ b/arch/cris/Kconfig @@ -13,10 +13,6 @@ config ZONE_DMA bool default y -config NO_DMA - bool - default y - config RWSEM_GENERIC_SPINLOCK bool default y @@ -24,6 +20,10 @@ config RWSEM_GENERIC_SPINLOCK config RWSEM_XCHGADD_ALGORITHM bool +config GENERIC_IOMAP + bool + default y + config ARCH_HAS_ILOG2_U32 bool default n @@ -44,17 +44,21 @@ config GENERIC_CALIBRATE_DELAY bool default y -config IRQ_PER_CPU - bool - default y - config NO_IOPORT def_bool y +config FORCE_MAX_ZONEORDER + int + default 6 + config CRIS bool default y +config HZ + int + default 100 + source "init/Kconfig" menu "General setup" @@ -93,17 +97,15 @@ config ETRAX_FAST_TIMER timers). This is needed if CONFIG_ETRAX_SERIAL_FAST_TIMER is enabled. -config PREEMPT - bool "Preemptible Kernel" +config ETRAX_KMALLOCED_MODULES + bool "Enable module allocation with kmalloc" help - This option reduces the latency of the kernel when reacting to - real-time or interactive events by allowing a low priority process to - be preempted even if it is in kernel mode executing a system call. - This allows applications to run more reliably even when the system is - under load. + Enable module allocation with kmalloc instead of vmalloc. + +config OOM_REBOOT + bool "Enable reboot at out of memory" - Say Y here if you are building a kernel for a desktop, embedded - or real-time system. Say N if you are unsure. +source "kernel/Kconfig.preempt" source mm/Kconfig @@ -130,24 +132,124 @@ config SVINTO_SIM help Support the xsim ETRAX Simulator. +config ETRAXFS + bool "ETRAX-FS-V32" + help + Support CRIS V32. + +config CRIS_MACH_ARTPEC3 + bool "ARTPEC-3" + help + Support Axis ARTPEC-3. + endchoice +config ETRAX_VCS_SIM + bool "VCS Simulator" + help + Setup hardware to be run in the VCS simulator. + config ETRAX_ARCH_V10 bool default y if ETRAX100LX || ETRAX100LX_V2 default n if !(ETRAX100LX || ETRAX100LX_V2) +config ETRAX_ARCH_V32 + bool + default y if (ETRAXFS || CRIS_MACH_ARTPEC3) + default n if !(ETRAXFS || CRIS_MACH_ARTPEC3) + config ETRAX_DRAM_SIZE int "DRAM size (dec, in MB)" default "8" help Size of DRAM (decimal in MB) typically 2, 8 or 16. +config ETRAX_VMEM_SIZE + int "Video memory size (dec, in MB)" + depends on ETRAX_ARCH_V32 && !ETRAXFS + default 8 if !ETRAXFS + help + Size of Video accessible memory (decimal, in MB). + config ETRAX_FLASH_BUSWIDTH - int "Buswidth of flash in bytes" + int "Buswidth of NOR flash in bytes" default "2" help - Width in bytes of the Flash bus (1, 2 or 4). Is usually 2. + Width in bytes of the NOR Flash bus (1, 2 or 4). Is usually 2. + +config ETRAX_NANDFLASH_BUSWIDTH + int "Buswidth of NAND flash in bytes" + default "1" + help + Width in bytes of the NAND flash (1 or 2). + +config ETRAX_FLASH1_SIZE + int "FLASH1 size (dec, in MB. 0 = Unknown)" + default "0" + +choice + prompt "Product debug-port" + default ETRAX_DEBUG_PORT0 + +config ETRAX_DEBUG_PORT0 + bool "Serial-0" + help + Choose a serial port for the ETRAX debug console. Default to + port 0. + +config ETRAX_DEBUG_PORT1 + bool "Serial-1" + help + Use serial port 1 for the console. + +config ETRAX_DEBUG_PORT2 + bool "Serial-2" + help + Use serial port 2 for the console. + +config ETRAX_DEBUG_PORT3 + bool "Serial-3" + help + Use serial port 3 for the console. + +config ETRAX_DEBUG_PORT_NULL + bool "disabled" + help + Disable serial-port debugging. + +endchoice + +choice + prompt "Kernel GDB port" + depends on ETRAX_KGDB + default ETRAX_KGDB_PORT0 + help + Choose a serial port for kernel debugging. NOTE: This port should + not be enabled under Drivers for built-in interfaces (as it has its + own initialization code) and should not be the same as the debug port. + +config ETRAX_KGDB_PORT0 + bool "Serial-0" + help + Use serial port 0 for kernel debugging. + +config ETRAX_KGDB_PORT1 + bool "Serial-1" + help + Use serial port 1 for kernel debugging. + +config ETRAX_KGDB_PORT2 + bool "Serial-2" + help + Use serial port 2 for kernel debugging. + +config ETRAX_KGDB_PORT3 + bool "Serial-3" + help + Use serial port 3 for kernel debugging. + +endchoice source arch/cris/arch-v10/Kconfig source arch/cris/arch-v32/Kconfig @@ -161,6 +263,387 @@ menu "Drivers for built-in interfaces" source arch/cris/arch-v10/drivers/Kconfig source arch/cris/arch-v32/drivers/Kconfig +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_JEDECPROBE if ETRAX_ARCH_V32 + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +config ETRAX_RTC + bool "Real Time Clock support" + depends on ETRAX_I2C + help + Enables drivers for the Real-Time Clock battery-backed chips on + some products. The kernel reads the time when booting, and + the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a + rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc + device. You can check the time with cat /proc/rtc, but + normal time reading should be done using libc function time and + friends. + +choice + prompt "RTC chip" + depends on ETRAX_RTC + default ETRAX_PCF8563 if ETRAX_ARCH_V32 + default ETRAX_DS1302 if ETRAX_ARCH_V10 + +config ETRAX_DS1302 + depends on ETRAX_ARCH_V10 + bool "DS1302" + help + Enables the driver for the DS1302 Real-Time Clock battery-backed + chip on some products. + +config ETRAX_PCF8563 + bool "PCF8563" + help + Enables the driver for the PCF8563 Real-Time Clock battery-backed + chip on some products. + +endchoice + +config ETRAX_SYNCHRONOUS_SERIAL + bool "Synchronous serial-port support" + help + Select this to enable the synchronous serial port driver. + +config ETRAX_SYNCHRONOUS_SERIAL_PORT0 + bool "Synchronous serial port 0 enabled" + depends on ETRAX_SYNCHRONOUS_SERIAL + help + Enabled synchronous serial port 0. + +config ETRAX_SYNCHRONOUS_SERIAL0_DMA + bool "Enable DMA on synchronous serial port 0." + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0 + help + A synchronous serial port can run in manual or DMA mode. + Selecting this option will make it run in DMA mode. + +config ETRAX_SYNCHRONOUS_SERIAL_PORT1 + bool "Synchronous serial port 1 enabled" + depends on ETRAX_SYNCHRONOUS_SERIAL && (ETRAXFS || ETRAX_ARCH_V10) + help + Enabled synchronous serial port 1. + +config ETRAX_SYNCHRONOUS_SERIAL1_DMA + bool "Enable DMA on synchronous serial port 1." + depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1 + help + A synchronous serial port can run in manual or DMA mode. + Selecting this option will make it run in DMA mode. + +choice + prompt "Network LED behavior" + depends on ETRAX_ETHERNET + default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + +config ETRAX_NETWORK_LED_ON_WHEN_LINK + bool "LED_on_when_link" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + bool "LED_on_when_activity" + help + Selecting LED_on_when_link will light the LED when there is a + connection and will flash off when there is activity. + + Selecting LED_on_when_activity will light the LED only when + there is activity. + + This setting will also affect the behaviour of other activity LEDs + e.g. Bluetooth. + +endchoice + +choice + prompt "Ser0 DMA out channel" + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_DMA6_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT0_NO_DMA_OUT if ETRAX_ARCH_V10 + +config ETRAX_SERIAL_PORT0_NO_DMA_OUT + bool "Ser0 uses no DMA for output" + help + Do not use DMA for ser0 output. + +config ETRAX_SERIAL_PORT0_DMA6_OUT + bool "Ser0 uses DMA6 for output" + depends on ETRAXFS + help + Enables the DMA6 output channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT0_DMA0_OUT + bool "Ser0 uses DMA0 for output" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA0 output channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser0 DMA in channel " + depends on ETRAX_SERIAL_PORT0 + default ETRAX_SERIAL_PORT0_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT0_DMA7_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser0. + +config ETRAX_SERIAL_PORT0_NO_DMA_IN + bool "Ser0 uses no DMA for input" + help + Do not use DMA for ser0 input. + +config ETRAX_SERIAL_PORT0_DMA7_IN + bool "Ser0 uses DMA7 for input" + depends on ETRAXFS + help + Enables the DMA7 input channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT0_DMA1_IN + bool "Ser0 uses DMA1 for input" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA1 input channel for ser0 (ttyS0). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser1 DMA in channel " + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT1_DMA9_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser1. + +config ETRAX_SERIAL_PORT1_NO_DMA_IN + bool "Ser1 uses no DMA for input" + help + Do not use DMA for ser1 input. + +config ETRAX_SERIAL_PORT1_DMA5_IN + bool "Ser1 uses DMA5 for input" + depends on ETRAX_ARCH_V32 + help + Enables the DMA5 input channel for ser1 (ttyS1). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want this on, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT1_DMA9_IN + depends on ETRAX_ARCH_V10 + bool "Ser1 uses DMA9 for input" + +endchoice + + +choice + prompt "Ser1 DMA out channel" + depends on ETRAX_SERIAL_PORT1 + default ETRAX_SERIAL_PORT1_NO_DMA_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT1_DMA8_OUT if ETRAX_ARCH_V10 + help + What DMA channel to use for ser1. + +config ETRAX_SERIAL_PORT1_NO_DMA_OUT + bool "Ser1 uses no DMA for output" + help + Do not use DMA for ser1 output. + +config ETRAX_SERIAL_PORT1_DMA8_OUT + depends on ETRAX_ARCH_V10 + bool "Ser1 uses DMA8 for output" + +config ETRAX_SERIAL_PORT1_DMA4_OUT + depends on ETRAX_ARCH_V32 + bool "Ser1 uses DMA4 for output" + help + Enables the DMA4 output channel for ser1 (ttyS1). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want this on, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser2 DMA out channel" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_NO_DMA_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT2_DMA2_OUT if ETRAX_ARCH_V10 + +config ETRAX_SERIAL_PORT2_NO_DMA_OUT + bool "Ser2 uses no DMA for output" + help + Do not use DMA for ser2 output. + +config ETRAX_SERIAL_PORT2_DMA2_OUT + bool "Ser2 uses DMA2 for output" + depends on ETRAXFS || ETRAX_ARCH_V10 + help + Enables the DMA2 output channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT2_DMA6_OUT + bool "Ser2 uses DMA6 for output" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA6 output channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser2 DMA in channel" + depends on ETRAX_SERIAL_PORT2 + default ETRAX_SERIAL_PORT2_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT2_DMA3_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser2. + +config ETRAX_SERIAL_PORT2_NO_DMA_IN + bool "Ser2 uses no DMA for input" + help + Do not use DMA for ser2 input. + +config ETRAX_SERIAL_PORT2_DMA3_IN + bool "Ser2 uses DMA3 for input" + depends on ETRAXFS || ETRAX_ARCH_V10 + help + Enables the DMA3 input channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT2_DMA7_IN + bool "Ser2 uses DMA7 for input" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA7 input channel for ser2 (ttyS2). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser3 DMA in channel" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_NO_DMA_IN if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT3_DMA5_IN if ETRAX_ARCH_V10 + help + What DMA channel to use for ser3. + +config ETRAX_SERIAL_PORT3_NO_DMA_IN + bool "Ser3 uses no DMA for input" + help + Do not use DMA for ser3 input. + +config ETRAX_SERIAL_PORT3_DMA5_IN + depends on ETRAX_ARCH_V10 + bool "DMA 5" + +config ETRAX_SERIAL_PORT3_DMA9_IN + bool "Ser3 uses DMA9 for input" + depends on ETRAXFS + help + Enables the DMA9 input channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when receiving data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT3_DMA3_IN + bool "Ser3 uses DMA3 for input" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA3 input channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when receiveing data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + +choice + prompt "Ser3 DMA out channel" + depends on ETRAX_SERIAL_PORT3 + default ETRAX_SERIAL_PORT3_NO_DMA_OUT if ETRAX_ARCH_V32 + default ETRAX_SERIAL_PORT3_DMA4_OUT if ETRAX_ARCH_V10 + +config ETRAX_SERIAL_PORT3_NO_DMA_OUT + bool "Ser3 uses no DMA for output" + help + Do not use DMA for ser3 output. + +config ETRAX_SERIAL_PORT3_DMA4_OUT + depends on ETRAX_ARCH_V10 + bool "DMA 4" + +config ETRAX_SERIAL_PORT3_DMA8_OUT + bool "Ser3 uses DMA8 for output" + depends on ETRAXFS + help + Enables the DMA8 output channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +config ETRAX_SERIAL_PORT3_DMA2_OUT + bool "Ser3 uses DMA2 for output" + depends on CRIS_MACH_ARTPEC3 + help + Enables the DMA2 output channel for ser3 (ttyS3). + If you do not enable DMA, an interrupt for each character will be + used when transmitting data. + Normally you want to use DMA, unless you use the DMA channel for + something else. + +endchoice + endmenu source "drivers/base/Kconfig" @@ -174,22 +657,10 @@ source "drivers/pnp/Kconfig" source "drivers/block/Kconfig" -source "drivers/md/Kconfig" - source "drivers/ide/Kconfig" -source "drivers/scsi/Kconfig" - -source "drivers/ieee1394/Kconfig" - -source "drivers/message/i2o/Kconfig" - source "drivers/net/Kconfig" -source "drivers/isdn/Kconfig" - -source "drivers/telephony/Kconfig" - source "drivers/i2c/Kconfig" source "drivers/rtc/Kconfig" @@ -201,17 +672,8 @@ source "drivers/input/Kconfig" source "drivers/char/Kconfig" -#source drivers/misc/Config.in -source "drivers/media/Kconfig" - source "fs/Kconfig" -source "sound/Kconfig" - -source "drivers/pcmcia/Kconfig" - -source "drivers/pci/Kconfig" - source "drivers/usb/Kconfig" source "arch/cris/Kconfig.debug" diff --git a/arch/cris/Makefile b/arch/cris/Makefile index e6bf00c262e..838cd2ae03a 100644 --- a/arch/cris/Makefile +++ b/arch/cris/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.28 2005/03/17 10:44:37 larsv Exp $ +# # cris/Makefile # # This file is included by the global makefile so that you can add your own @@ -10,28 +10,36 @@ # License. See the file "COPYING" in the main directory of this archive # for more details. -# A bug in ld prevents us from having a (constant-value) symbol in a -# "ORIGIN =" or "LENGTH =" expression. - arch-y := v10 arch-$(CONFIG_ETRAX_ARCH_V10) := v10 arch-$(CONFIG_ETRAX_ARCH_V32) := v32 -# No config avaiable for make clean etc +# No config available for make clean etc +mach-y := fs +mach-$(CONFIG_CRIS_MACH_ARTPEC3) := a3 +mach-$(CONFIG_ETRAXFS) := fs + ifneq ($(arch-y),) SARCH := arch-$(arch-y) else SARCH := endif +ifneq ($(mach-y),) +MACH := mach-$(mach-y) +else +MACH := +endif + LD = $(CROSS_COMPILE)ld -mcrislinux OBJCOPYFLAGS := -O binary -R .note -R .comment -S CPPFLAGS_vmlinux.lds = -DDRAM_VIRTUAL_BASE=0x$(CONFIG_ETRAX_DRAM_VIRTUAL_BASE) -KBUILD_AFLAGS += -mlinux -KBUILD_CFLAGS += -mlinux -march=$(arch-y) -pipe +KBUILD_AFLAGS += -mlinux -march=$(arch-y) -Iinclude/asm/arch/mach -Iinclude/asm/arch + +KBUILD_CFLAGS += -mlinux -march=$(arch-y) -pipe -Iinclude/asm/arch/mach -Iinclude/asm/arch ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS := $(subst -fomit-frame-pointer,,$(KBUILD_CFLAGS)) -g @@ -44,6 +52,9 @@ LIBGCC = $(shell $(CC) $(KBUILD_CFLAGS) -print-file-name=libgcc.a) core-y += arch/$(ARCH)/kernel/ arch/$(ARCH)/mm/ core-y += arch/$(ARCH)/$(SARCH)/kernel/ arch/$(ARCH)/$(SARCH)/mm/ +ifdef CONFIG_ETRAX_ARCH_V32 +core-y += arch/$(ARCH)/$(SARCH)/$(MACH)/ +endif drivers-y += arch/$(ARCH)/$(SARCH)/drivers/ libs-y += arch/$(ARCH)/$(SARCH)/lib/ $(LIBGCC) @@ -52,79 +63,69 @@ SRC_ARCH = $(srctree)/arch/$(ARCH) # cris object files path OBJ_ARCH = $(objtree)/arch/$(ARCH) -target_boot_arch_dir = $(OBJ_ARCH)/$(SARCH)/boot -target_boot_dir = $(OBJ_ARCH)/boot -src_boot_dir = $(SRC_ARCH)/boot -target_compressed_dir = $(OBJ_ARCH)/boot/compressed -src_compressed_dir = $(SRC_ARCH)/boot/compressed -target_rescue_dir = $(OBJ_ARCH)/boot/rescue -src_rescue_dir = $(SRC_ARCH)/boot/rescue - -export target_boot_arch_dir target_boot_dir src_boot_dir target_compressed_dir src_compressed_dir target_rescue_dir src_rescue_dir - -vmlinux.bin: vmlinux - $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin - -timage: vmlinux.bin - cat vmlinux.bin cramfs.img >timage - -simimage: timage - cp vmlinux.bin simvmlinux.bin - -# the following will remake timage without compiling the kernel -# it does of course require that all object files exist... - -cramfs: -## cramfs - Creates a cramfs image - mkcramfs -b 8192 -m romfs_meta.txt root cramfs.img - cat vmlinux.bin cramfs.img >timage +boot := arch/$(ARCH)/boot +MACHINE := arch/$(ARCH)/$(SARCH) -clinux: vmlinux.bin decompress.bin rescue.bin +all: zImage -decompress.bin: $(target_boot_dir) - @$(MAKE) -f $(src_compressed_dir)/Makefile $(target_compressed_dir)/decompress.bin +zImage Image: vmlinux + $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ -$(target_rescue_dir)/rescue.bin: $(target_boot_dir) - @$(MAKE) -f $(src_rescue_dir)/Makefile $(target_rescue_dir)/rescue.bin - -zImage: $(target_boot_dir) vmlinux.bin $(target_rescue_dir)/rescue.bin -## zImage - Compressed kernel (gzip) - @$(MAKE) -f $(src_boot_dir)/Makefile zImage - -$(target_boot_dir): $(target_boot_arch_dir) - ln -sfn $< $@ - -$(target_boot_arch_dir): - mkdir -p $@ - -compressed: zImage - -archmrproper: -archclean: - @if [ -d arch/$(ARCH)/boot ]; then \ - $(MAKE) $(clean)=arch/$(ARCH)/boot ; \ - fi - rm -f timage vmlinux.bin decompress.bin rescue.bin cramfs.img - rm -rf $(LD_SCRIPT).tmp - -archprepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch +archprepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch FORCE # Create some links to make all tools happy $(SRC_ARCH)/.links: @rm -rf $(SRC_ARCH)/drivers - @ln -sfn $(SRC_ARCH)/$(SARCH)/drivers $(SRC_ARCH)/drivers + @ln -sfn $(SARCH)/drivers $(SRC_ARCH)/drivers @rm -rf $(SRC_ARCH)/boot - @ln -sfn $(SRC_ARCH)/$(SARCH)/boot $(SRC_ARCH)/boot + @ln -sfn $(SARCH)/boot $(SRC_ARCH)/boot @rm -rf $(SRC_ARCH)/lib - @ln -sfn $(SRC_ARCH)/$(SARCH)/lib $(SRC_ARCH)/lib - @ln -sfn $(SRC_ARCH)/$(SARCH) $(SRC_ARCH)/arch - @ln -sfn $(SRC_ARCH)/$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S - @ln -sfn $(SRC_ARCH)/$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c + @ln -sfn $(SARCH)/lib $(SRC_ARCH)/lib + @rm -f $(SRC_ARCH)/arch/mach + @rm -rf $(SRC_ARCH)/arch + @ln -sfn $(SARCH) $(SRC_ARCH)/arch +ifdef CONFIG_ETRAX_ARCH_V32 + @ln -sfn ../$(SARCH)/$(MACH) $(SRC_ARCH)/arch/mach +endif + @rm -rf $(SRC_ARCH)/kernel/vmlinux.lds.S + @ln -sfn ../$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S + @rm -rf $(SRC_ARCH)/kernel/asm-offsets.c + @ln -sfn ../$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c @touch $@ # Create link to sub arch includes $(srctree)/include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h) - @echo ' Making $(srctree)/include/asm-$(ARCH)/arch -> $(srctree)/include/asm-$(ARCH)/$(SARCH) symlink' - @rm -f include/asm-$(ARCH)/arch - @ln -sf $(srctree)/include/asm-$(ARCH)/$(SARCH) $(srctree)/include/asm-$(ARCH)/arch + @echo ' SYMLINK include/asm-$(ARCH)/arch -> include/asm-$(ARCH)/$(SARCH)' + @rm -f $(srctree)/include/asm-$(ARCH)/arch/mach + @rm -f $(srctree)/include/asm-$(ARCH)/arch + @ln -sf $(SARCH) $(srctree)/include/asm-$(ARCH)/arch +ifdef CONFIG_ETRAX_ARCH_V32 + @ln -sf $(MACH) $(srctree)/include/asm-$(ARCH)/arch/mach +endif @touch $@ + +archclean: + $(Q)if [ -e arch/$(ARCH)/boot ]; then \ + $(MAKE) $(clean)=arch/$(ARCH)/boot; \ + fi + +CLEAN_FILES += \ + $(MACHINE)/boot/zImage \ + $(MACHINE)/boot/compressed/decompress.bin \ + $(MACHINE)/boot/compressed/piggy.gz \ + $(MACHINE)/boot/rescue/rescue.bin \ + $(SRC_ARCH)/.links \ + $(srctree)/include/asm-$(ARCH)/.arch + +MRPROPER_FILES += \ + $(SRC_ARCH)/drivers \ + $(SRC_ARCH)/boot \ + $(SRC_ARCH)/lib \ + $(SRC_ARCH)/arch \ + $(SRC_ARCH)/kernel/vmlinux.lds.S \ + $(SRC_ARCH)/kernel/asm-offsets.c + +define archhelp + echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)' + echo '* Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)' +endef diff --git a/arch/cris/arch-v10/Kconfig b/arch/cris/arch-v10/Kconfig index 1d61faec77c..adc164e9933 100644 --- a/arch/cris/arch-v10/Kconfig +++ b/arch/cris/arch-v10/Kconfig @@ -1,5 +1,7 @@ if ETRAX_ARCH_V10 +menu "CRIS v10 options" + # ETRAX 100LX v1 has a MMU "feature" requiring a low mapping config CRIS_LOW_MAP bool @@ -228,69 +230,6 @@ config ETRAX_LED12R For products with only one or two controllable LEDs, set this to same as CONFIG_ETRAX_LED1G (normally 2). -choice - prompt "Product debug-port" - depends on ETRAX_ARCH_V10 - default ETRAX_DEBUG_PORT0 - -config ETRAX_DEBUG_PORT0 - bool "Serial-0" - help - Choose a serial port for the ETRAX debug console. Default to - port 0. - -config ETRAX_DEBUG_PORT1 - bool "Serial-1" - help - Use serial port 1 for the console. - -config ETRAX_DEBUG_PORT2 - bool "Serial-2" - help - Use serial port 2 for the console. - -config ETRAX_DEBUG_PORT3 - bool "Serial-3" - help - Use serial port 3 for the console. - -config ETRAX_DEBUG_PORT_NULL - bool "disabled" - help - Disable serial-port debugging. - -endchoice - -choice - prompt "Kernel GDB port" - depends on ETRAX_KGDB - default ETRAX_KGDB_PORT0 - help - Choose a serial port for kernel debugging. NOTE: This port should - not be enabled under Drivers for built-in interfaces (as it has its - own initialization code) and should not be the same as the debug port. - -config ETRAX_KGDB_PORT0 - bool "Serial-0" - help - Use serial port 0 for kernel debugging. - -config ETRAX_KGDB_PORT1 - bool "Serial-1" - help - Use serial port 1 for kernel debugging. - -config ETRAX_KGDB_PORT2 - bool "Serial-2" - help - Use serial port 2 for kernel debugging. - -config ETRAX_KGDB_PORT3 - bool "Serial-3" - help - Use serial port 3 for kernel debugging. - -endchoice choice prompt "Product rescue-port" @@ -454,4 +393,6 @@ config ETRAX_POWERBUTTON_BIT help Configure where power button is connected. +endmenu + endif diff --git a/arch/cris/arch-v10/boot/Makefile b/arch/cris/arch-v10/boot/Makefile index e5b10585110..20c83a53caf 100644 --- a/arch/cris/arch-v10/boot/Makefile +++ b/arch/cris/arch-v10/boot/Makefile @@ -1,13 +1,21 @@ # -# arch/cris/boot/Makefile +# arch/cris/arch-v10/boot/Makefile # -target = $(target_boot_dir) -src = $(src_boot_dir) -zImage: compressed/vmlinuz +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary --remove-section=.bss -compressed/vmlinuz: - @$(MAKE) -f $(src)/compressed/Makefile $(target_compressed_dir)/vmlinuz +subdir- := compressed rescue +targets := Image -clean: - @$(MAKE) -f $(src)/compressed/Makefile clean +$(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + @echo ' Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/Image FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin + +$(obj)/zImage: $(obj)/compressed/vmlinux + @cp $< $@ + @echo ' Kernel: $@ is ready' diff --git a/arch/cris/arch-v10/boot/compressed/Makefile b/arch/cris/arch-v10/boot/compressed/Makefile index 6584a44820f..4a031cb27eb 100644 --- a/arch/cris/arch-v10/boot/compressed/Makefile +++ b/arch/cris/arch-v10/boot/compressed/Makefile @@ -1,45 +1,35 @@ # -# create a compressed vmlinuz image from the binary vmlinux.bin file +# arch/cris/arch-v10/boot/compressed/Makefile # -target = $(target_compressed_dir) -src = $(src_compressed_dir) CC = gcc-cris -melf $(LINUXINCLUDE) -CFLAGS = -O2 +ccflags-y += -O2 LD = ld-cris +ldflags-y += -T $(obj)/decompress.ld +OBJECTS = $(obj)/head.o $(obj)/misc.o OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = $(target)/head.o $(target)/misc.o -# files to compress -SYSTEM = $(objtree)/vmlinux.bin +quiet_cmd_image = BUILD $@ +cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ -all: $(target_compressed_dir)/vmlinuz +targets := vmlinux piggy.gz decompress.o decompress.bin -$(target)/decompress.bin: $(OBJECTS) - $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin +$(obj)/decompress.o: $(OBJECTS) FORCE + $(call if_changed,ld) -# Create vmlinuz image in top-level build directory -$(target_compressed_dir)/vmlinuz: $(target) piggy.img $(target)/decompress.bin - @echo " COMPR vmlinux.bin --> vmlinuz" - @cat $(target)/decompress.bin piggy.img > $(target_compressed_dir)/vmlinuz - @rm -f piggy.img +$(obj)/decompress.bin: $(obj)/decompress.o FORCE + $(call if_changed,objcopy) -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ +$(obj)/head.o: $(obj)/head.S .config + @$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@ -$(target)/misc.o: $(src)/misc.c - $(CC) -D__KERNEL__ -c $< -o $@ +$(obj)/misc.o: $(obj)/misc.c .config + @$(CC) -D__KERNEL__ -c $< -o $@ -# gzip the kernel image +$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE + $(call if_changed,image) -piggy.img: $(SYSTEM) - @cat $(SYSTEM) | gzip -f -9 > piggy.img - -$(target): - mkdir -p $(target) - -clean: - rm -f piggy.img $(objtree)/vmlinuz +$(obj)/piggy.gz: $(obj)/../Image FORCE + $(call if_changed,gzip) diff --git a/arch/cris/arch-v10/boot/compressed/misc.c b/arch/cris/arch-v10/boot/compressed/misc.c index e205d2e7e08..9a43ab19391 100644 --- a/arch/cris/arch-v10/boot/compressed/misc.c +++ b/arch/cris/arch-v10/boot/compressed/misc.c @@ -1,15 +1,13 @@ /* * misc.c * - * $Id: misc.c,v 1.6 2003/10/27 08:04:31 starvik Exp $ - * - * This is a collection of several routines from gzip-1.0.3 + * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. * * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 * puts by Nick Holloway 1993, better puts by Martin Mares 1995 * adaptation for Linux/CRIS Axis Communications AB, 1999 - * + * */ /* where the piggybacked kernel image expects itself to live. diff --git a/arch/cris/arch-v10/boot/rescue/Makefile b/arch/cris/arch-v10/boot/rescue/Makefile index 8be9b313031..2e5045b9e19 100644 --- a/arch/cris/arch-v10/boot/rescue/Makefile +++ b/arch/cris/arch-v10/boot/rescue/Makefile @@ -1,56 +1,38 @@ # -# Makefile for rescue code +# Makefile for rescue (bootstrap) code # -target = $(target_rescue_dir) -src = $(src_rescue_dir) CC = gcc-cris -mlinux $(LINUXINCLUDE) -CFLAGS = -O2 +ccflags-y += -O2 +asflags-y += -traditional LD = gcc-cris -mlinux -nostdlib +ldflags-y += -T $(obj)/rescue.ld OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss +obj-$(CONFIG_ETRAX_AXISFLASHMAP) = head.o +OBJECT := $(obj)/head.o -all: $(target)/rescue.bin $(target)/testrescue.bin $(target)/kimagerescue.bin +targets := rescue.o rescue.bin -$(target)/rescue.bin: $(target) $(target)/head.o - $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin -# Place a copy in top-level build directory - cp -p $(target)/rescue.bin $(objtree) +$(obj)/rescue.o: $(OBJECT) FORCE + $(call if_changed,ld) -$(target)/testrescue.bin: $(target) $(target)/testrescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/testrescue.o tr.bin +$(obj)/rescue.bin: $(obj)/rescue.o FORCE + $(call if_changed,objcopy) + cp -p $(obj)/rescue.bin $(objtree) + +$(obj)/testrescue.bin: $(obj)/testrescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/testrescue.o tr.bin # Pad it to 784 bytes dd if=/dev/zero of=tmp2423 bs=1 count=784 cat tr.bin tmp2423 >testrescue_tmp.bin - dd if=testrescue_tmp.bin of=$(target)/testrescue.bin bs=1 count=784 + dd if=testrescue_tmp.bin of=$(obj)/testrescue.bin bs=1 count=784 rm tr.bin tmp2423 testrescue_tmp.bin -$(target)/kimagerescue.bin: $(target) $(target)/kimagerescue.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/kimagerescue.o ktr.bin +$(obj)/kimagerescue.bin: $(obj)/kimagerescue.o + $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/kimagerescue.o ktr.bin # Pad it to 784 bytes, that's what the rescue loader expects dd if=/dev/zero of=tmp2423 bs=1 count=784 cat ktr.bin tmp2423 >kimagerescue_tmp.bin - dd if=kimagerescue_tmp.bin of=$(target)/kimagerescue.bin bs=1 count=784 + dd if=kimagerescue_tmp.bin of=$(obj)/kimagerescue.bin bs=1 count=784 rm ktr.bin tmp2423 kimagerescue_tmp.bin - -$(target): - mkdir -p $(target) - -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -$(target)/testrescue.o: $(src)/testrescue.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -$(target)/kimagerescue.o: $(src)/kimagerescue.S - $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o - -clean: - rm -f $(target)/*.o $(target)/*.bin - -fastdep: - -modules: - -modules-install: diff --git a/arch/cris/arch-v10/boot/rescue/head.S b/arch/cris/arch-v10/boot/rescue/head.S index f223cc0c00b..6ba7be8ac4a 100644 --- a/arch/cris/arch-v10/boot/rescue/head.S +++ b/arch/cris/arch-v10/boot/rescue/head.S @@ -1,5 +1,4 @@ -/* $Id: head.S,v 1.7 2005/03/07 12:11:06 starvik Exp $ - * +/* * Rescue code, made to reside at the beginning of the * flash-memory. when it starts, it checks a partition * table at the first sector after the rescue sector. @@ -23,20 +22,20 @@ * Partition table format: * * Code transparency: - * + * * 2 bytes [opcode 'nop'] * 2 bytes [opcode 'di'] * 4 bytes [opcode 'ba <offset>', 8-bit or 16-bit version] * 2 bytes [opcode 'nop', delay slot] * - * Table validation (at +10): - * + * Table validation (at +10): + * * 2 bytes [magic/version word for partitiontable - 0xef, 0xbe] * 2 bytes [length of all entries plus the end marker] * 4 bytes [checksum for the partitiontable itself] * - * Entries, each with the following format, last has offset -1: - * + * Entries, each with the following format, last has offset -1: + * * 4 bytes [offset in bytes, from start of flash] * 4 bytes [length in bytes of partition] * 4 bytes [checksum, simple longword sum] @@ -47,9 +46,9 @@ * End marker * * 4 bytes [-1] - * + * * 10 bytes [0, padding] - * + * * Bit 0 in flags signifies RW or RO. The rescue code only bothers * to check the checksum for RO partitions, since the others will * change their data without updating the checksums. A 1 in bit 0 @@ -59,26 +58,29 @@ * * During the wait for serial input, the status LED will flash so the * user knows something went wrong. - * - * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + * + * Copyright (C) 1999-2007 Axis Communications AB */ +#ifdef CONFIG_ETRAX_AXISFLASHMAP + #define ASSEMBLER_MACROS_ONLY #include <asm/arch/sv_addr_ag.h> ;; The partitiontable is looked for at the first sector after the boot ;; sector. Sector size is 65536 bytes in all flashes we use. - + #define PTABLE_START CONFIG_ETRAX_PTABLE_SECTOR #define PTABLE_MAGIC 0xbeef ;; The normal Etrax100 on-chip boot ROM does serial boot at 0x380000f0. - ;; That is not where we put our downloaded serial boot-code. The length is - ;; enough for downloading code that loads the rest of itself (after - ;; having setup the DRAM etc). It is the same length as the on-chip - ;; ROM loads, so the same host loader can be used to load a rescued - ;; product as well as one booted through the Etrax serial boot code. - + ;; That is not where we put our downloaded serial boot-code. + ;; The length is enough for downloading code that loads the rest + ;; of itself (after having setup the DRAM etc). + ;; It is the same length as the on-chip ROM loads, so the same + ;; host loader can be used to load a rescued product as well as + ;; one booted through the Etrax serial boot code. + #define CODE_START 0x40000000 #define CODE_LENGTH 784 @@ -102,7 +104,7 @@ #define SERRECC R_SERIAL2_REC_CTRL #define SERRDAT R_SERIAL2_REC_DATA #define SERSTAT R_SERIAL2_STATUS -#endif +#endif #ifdef CONFIG_ETRAX_RESCUE_SER3 #define SERXOFF R_SERIAL3_XOFF #define SERBAUD R_SERIAL3_BAUD @@ -115,60 +117,61 @@ #define RAM_INIT_MAGIC 0x56902387 .text - + ;; This is the entry point of the rescue code ;; 0x80000000 if loaded in flash (as it should be) - ;; since etrax actually starts at address 2 when booting from flash, we + ;; Since etrax actually starts at address 2 when booting from flash, we ;; put a nop (2 bytes) here first so we dont accidentally skip the di - nop + nop di jump in_cache ; enter cached area instead in_cache: - ;; first put a jump test to give a possibility of upgrading the rescue code - ;; without erasing/reflashing the sector. we put a longword of -1 here and if - ;; it is not -1, we jump using the value as jump target. since we can always - ;; change 1's to 0's without erasing the sector, it is possible to add new + ;; First put a jump test to give a possibility of upgrading the + ;; rescue code without erasing/reflashing the sector. + ;; We put a longword of -1 here and if it is not -1, we jump using + ;; the value as jump target. Since we can always change 1's to 0's + ;; without erasing the sector, it is possible to add new ;; code after this and altering the jumptarget in an upgrade. jtcd: move.d [jumptarget], $r0 cmp.d 0xffffffff, $r0 beq no_newjump nop - + jump [$r0] -jumptarget: +jumptarget: .dword 0xffffffff ; can be overwritten later to insert new code - + no_newjump: -#ifdef CONFIG_ETRAX_ETHERNET +#ifdef CONFIG_ETRAX_ETHERNET ;; Start MII clock to make sure it is running when tranceiver is reset move.d 0x3, $r0 ; enable = on, phy = mii_clk move.d $r0, [R_NETWORK_GEN_CONFIG] #endif - + ;; We need to setup the bus registers before we start using the DRAM #include "../../lib/dram_init.S" ;; we now should go through the checksum-table and check the listed ;; partitions for errors. - + move.d PTABLE_START, $r3 move.d [$r3], $r0 cmp.d NOP_DI, $r0 ; make sure the nop/di is there... bne do_rescue nop - + ;; skip the code transparency block (10 bytes). addq 10, $r3 - + ;; check for correct magic - + move.w [$r3+], $r0 cmp.w PTABLE_MAGIC, $r0 bne do_rescue ; didn't recognize - trig rescue @@ -186,11 +189,11 @@ no_newjump: cmp.d $r0, $r4 bne do_rescue ; didn't match - trig rescue nop - + ;; ptable is ok. validate each entry. moveq -1, $r7 - + ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) bne notfirst ; check if it is the partition containing ptable nop ; yes.. @@ -199,7 +202,7 @@ ploop: move.d [$r3+], $r1 ; partition offset (from ptable start) sub.d $r8, $r2 ; minus the ptable length ba bosse nop -notfirst: +notfirst: cmp.d -1, $r1 ; the end of the ptable ? beq flash_ok ; if so, the flash is validated move.d [$r3+], $r2 ; partition length @@ -213,47 +216,46 @@ bosse: move.d [$r3+], $r5 ; checksum bpl 1f nop move.d $r1, $r7 ; remember boot partition offset -1: - +1: add.d PTABLE_START, $r1 - + jsr checksum ; checksum the partition - + cmp.d $r0, $r5 beq ploop ; checksums matched, go to next entry nop ;; otherwise fall through to the rescue code. - + do_rescue: ;; setup port PA and PB default initial directions and data ;; (so we can flash LEDs, and so that DTR and others are set) - + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 move.b $r0, [R_PORT_PA_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 move.b $r0, [R_PORT_PA_DATA] - + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 move.b $r0, [R_PORT_PB_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 move.b $r0, [R_PORT_PB_DATA] ;; setup the serial port at 115200 baud - + moveq 0, $r0 - move.d $r0, [SERXOFF] + move.d $r0, [SERXOFF] move.b 0x99, $r0 - move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive + move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive - move.b 0x40, $r0 ; rec enable - move.b $r0, [SERRECC] + move.b 0x40, $r0 ; rec enable + move.b $r0, [SERRECC] moveq 0, $r1 ; "timer" to clock out a LED red flash move.d CODE_START, $r3 ; destination counter movu.w CODE_LENGTH, $r4; length - + wait_ser: addq 1, $r1 #ifndef CONFIG_ETRAX_NO_LEDS @@ -272,20 +274,20 @@ wait_ser: nop 1: not $r0 ; clear bit and.d $r0, $r2 -2: +2: #ifdef CONFIG_ETRAX_PA_LEDS - move.b $r2, [R_PORT_PA_DATA] -#endif + move.b $r2, [R_PORT_PA_DATA] +#endif #ifdef CONFIG_ETRAX_PB_LEDS - move.b $r2, [R_PORT_PB_DATA] + move.b $r2, [R_PORT_PB_DATA] #endif #ifdef CONFIG_ETRAX_90000000_LEDS move.b $r2, [0x90000000] #endif #endif - + ;; check if we got something on the serial port - + move.b [SERSTAT], $r0 btstq 0, $r0 ; data_avail bpl wait_ser @@ -295,14 +297,15 @@ wait_ser: move.b [SERRDAT], $r0 move.b $r0, [$r3+] - + subq 1, $r4 ; decrease length bne wait_ser nop ;; jump into downloaded code - move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized + move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is + ; initialized jump CODE_START flash_ok: @@ -313,7 +316,8 @@ flash_ok: nop move.d PTABLE_START, $r7; otherwise use the ptable start 1: - move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is initialized + move.d RAM_INIT_MAGIC, $r8 ; Tell next product that DRAM is + ; initialized jump $r7 ; boot! @@ -327,7 +331,8 @@ checksum: moveq 0, $r0 moveq CONFIG_ETRAX_FLASH1_SIZE, $r6 - ;; If the first physical flash memory is exceeded wrap to the second one. + ;; If the first physical flash memory is exceeded wrap to the + ;; second one btstq 26, $r1 ; Are we addressing first flash? bpl 1f nop @@ -351,3 +356,5 @@ checksum: 3: move.d MEM_CSE1_START, $r1 ; wrap to second flash ba 2b nop + +#endif diff --git a/arch/cris/arch-v10/boot/rescue/kimagerescue.S b/arch/cris/arch-v10/boot/rescue/kimagerescue.S index cbccd6316d3..55eeff8bb08 100644 --- a/arch/cris/arch-v10/boot/rescue/kimagerescue.S +++ b/arch/cris/arch-v10/boot/rescue/kimagerescue.S @@ -1,5 +1,4 @@ -/* $Id: kimagerescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ - * +/* * Rescue code to be prepended on a kimage and copied to the * rescue serial port. * This is called from the rescue code, it will copy received data to @@ -7,13 +6,13 @@ */ #define ASSEMBLER_MACROS_ONLY -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> #define CODE_START 0x40004000 #define CODE_LENGTH 784 #define TIMEOUT_VALUE 1000 - - + + #ifdef CONFIG_ETRAX_RESCUE_SER0 #define SERXOFF R_SERIAL0_XOFF #define SERBAUD R_SERIAL0_BAUD @@ -34,7 +33,7 @@ #define SERRECC R_SERIAL2_REC_CTRL #define SERRDAT R_SERIAL2_REC_DATA #define SERSTAT R_SERIAL2_STATUS -#endif +#endif #ifdef CONFIG_ETRAX_RESCUE_SER3 #define SERXOFF R_SERIAL3_XOFF #define SERBAUD R_SERIAL3_BAUD @@ -48,54 +47,55 @@ ;; 0x80000000 if loaded in flash (as it should be) ;; since etrax actually starts at address 2 when booting from flash, we ;; put a nop (2 bytes) here first so we dont accidentally skip the di - - nop + + nop di -#ifndef CONFIG_SVINTO_SIM +#ifndef CONFIG_SVINTO_SIM ;; setup port PA and PB default initial directions and data ;; (so we can flash LEDs, and so that DTR and others are set) - + move.b CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0 move.b $r0, [R_PORT_PA_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0 move.b $r0, [R_PORT_PA_DATA] - + move.b CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0 move.b $r0, [R_PORT_PB_DIR] move.b CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0 move.b $r0, [R_PORT_PB_DATA] - + ;; We need to setup the bus registers before we start using the DRAM #include "../../lib/dram_init.S" - + #endif ;; Setup the stack to a suitably high address. ;; We assume 8 MB is the minimum DRAM in an eLinux ;; product and put the sp at the top for now. move.d 0x40800000, $sp - + ;; setup the serial port at 115200 baud - + moveq 0, $r0 - move.d $r0, [SERXOFF] + move.d $r0, [SERXOFF] move.b 0x99, $r0 - move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit and receive + move.b $r0, [SERBAUD] ; 115.2kbaud for both transmit + ; and receive move.b 0x40, $r0 ; rec enable - move.b $r0, [SERRECC] + move.b $r0, [SERRECC] - moveq 0, $r1 ; "timer" to clock out a LED red flash - move.d CODE_START, $r3 ; destination counter + moveq 0, $r1 ; "timer" to clock out a LED red flash + move.d CODE_START, $r3 ; destination counter move.d CODE_LENGTH, $r4 ; length move.d TIMEOUT_VALUE, $r5 ; "timeout" until jump wait_ser: addq 1, $r1 - subq 1, $r5 ; decrease timeout - beq jump_start ; timed out + subq 1, $r5 ; decrease timeout + beq jump_start ; timed out nop #ifndef CONFIG_ETRAX_NO_LEDS #ifdef CONFIG_ETRAX_PA_LEDS @@ -111,21 +111,21 @@ wait_ser: or.d $r0, $r2 ; set bit ba 2f nop -1: not $r0 ; clear bit +1: not $r0 ; clear bit and.d $r0, $r2 -2: +2: #ifdef CONFIG_ETRAX_PA_LEDS move.b $r2, [R_PORT_PA_DATA] -#endif +#endif #ifdef CONFIG_ETRAX_PB_LEDS move.b $r2, [R_PORT_PB_DATA] #endif #endif - + ;; check if we got something on the serial port - + move.b [SERSTAT], $r0 - btstq 0, $r0 ; data_avail + btstq 0, $r0 ; data_avail bpl wait_ser nop @@ -134,7 +134,7 @@ wait_ser: move.b [SERRDAT], $r0 move.b $r0, [$r3+] move.d TIMEOUT_VALUE, $r5 ; reset "timeout" - subq 1, $r4 ; decrease length + subq 1, $r4 ; decrease length bne wait_ser nop jump_start: diff --git a/arch/cris/arch-v10/boot/rescue/testrescue.S b/arch/cris/arch-v10/boot/rescue/testrescue.S index 566a9f34125..2d937f9afe2 100644 --- a/arch/cris/arch-v10/boot/rescue/testrescue.S +++ b/arch/cris/arch-v10/boot/rescue/testrescue.S @@ -1,13 +1,12 @@ -/* $Id: testrescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ - * +/* * Simple testcode to download by the rescue block. - * Just lits some LEDs to show it was downloaded correctly. - * + * Just lights some LEDs to show it was downloaded correctly. + * * Copyright (C) 1999 Axis Communications AB */ #define ASSEMBLER_MACROS_ONLY -#include <asm/sv_addr_ag.h> +#include <asm/arch/sv_addr_ag.h> .text @@ -16,11 +15,10 @@ moveq -1, $r2 move.b $r2, [R_PORT_PA_DIR] moveq 0, $r2 - move.b $r2, [R_PORT_PA_DATA] + move.b $r2, [R_PORT_PA_DATA] endless: nop ba endless nop - diff --git a/arch/cris/arch-v10/drivers/Kconfig b/arch/cris/arch-v10/drivers/Kconfig index 96740ef497d..58f5864a668 100644 --- a/arch/cris/arch-v10/drivers/Kconfig +++ b/arch/cris/arch-v10/drivers/Kconfig @@ -9,37 +9,6 @@ config ETRAX_ETHERNET This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet controller. -choice - prompt "Network LED behavior" - depends on ETRAX_ETHERNET - default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - -config ETRAX_NETWORK_LED_ON_WHEN_LINK - bool "LED_on_when_link" - help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. - - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. - -config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - bool "LED_on_when_activity" - help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. - - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. - -endchoice - config ETRAX_SERIAL bool "Serial-port support" depends on ETRAX_ARCH_V10 @@ -84,32 +53,6 @@ config ETRAX_SERIAL_PORT0 the same DMA channels. choice - prompt "Ser0 DMA out assignment" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_DMA6_OUT - -config ETRAX_SERIAL_PORT0_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT0_DMA6_OUT - bool "DMA 6" - -endchoice - -choice - prompt "Ser0 DMA in assignment" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_DMA7_IN - -config ETRAX_SERIAL_PORT0_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT0_DMA7_IN - bool "DMA 7" - -endchoice - -choice prompt "Ser0 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT0 default ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE @@ -198,32 +141,6 @@ config ETRAX_SERIAL_PORT1 Enables the ETRAX 100 serial driver for ser1 (ttyS1). choice - prompt "Ser1 DMA out assignment" - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_DMA8_OUT - -config ETRAX_SERIAL_PORT1_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT1_DMA8_OUT - bool "DMA 8" - -endchoice - -choice - prompt "Ser1 DMA in assignment" - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_DMA9_IN - -config ETRAX_SERIAL_PORT1_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT1_DMA9_IN - bool "DMA 9" - -endchoice - -choice prompt "Ser1 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT1 default ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE @@ -315,32 +232,6 @@ config ETRAX_SERIAL_PORT2 Enables the ETRAX 100 serial driver for ser2 (ttyS2). choice - prompt "Ser2 DMA out assignment" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_DMA2_OUT - -config ETRAX_SERIAL_PORT2_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT2_DMA2_OUT - bool "DMA 2" - -endchoice - -choice - prompt "Ser2 DMA in assignment" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_DMA3_IN - -config ETRAX_SERIAL_PORT2_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT2_DMA3_IN - bool "DMA 3" - -endchoice - -choice prompt "Ser2 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT2 default ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE @@ -429,32 +320,6 @@ config ETRAX_SERIAL_PORT3 Enables the ETRAX 100 serial driver for ser3 (ttyS3). choice - prompt "Ser3 DMA out assignment" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_DMA4_OUT - -config ETRAX_SERIAL_PORT3_NO_DMA_OUT - bool "No DMA out" - -config ETRAX_SERIAL_PORT3_DMA4_OUT - bool "DMA 4" - -endchoice - -choice - prompt "Ser3 DMA in assignment" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_DMA5_IN - -config ETRAX_SERIAL_PORT3_NO_DMA_IN - bool "No DMA in" - -config ETRAX_SERIAL_PORT3_DMA5_IN - bool "DMA 5" - -endchoice - -choice prompt "Ser3 DTR, RI, DSR and CD assignment" depends on ETRAX_SERIAL_PORT3 default ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE @@ -563,21 +428,6 @@ config ETRAX_USB_HOST_PORT2 depends on ETRAX_USB_HOST default n -config ETRAX_AXISFLASHMAP - bool "Axis flash-map support" - depends on ETRAX_ARCH_V10 - select MTD - select MTD_CFI - select MTD_CFI_AMDSTD - select MTD_CHAR - select MTD_BLOCK - select MTD_PARTITIONS - select MTD_CONCAT - select MTD_COMPLEX_MAPPINGS - help - This option enables MTD mapping of flash devices. Needed to use - flash memories. If unsure, say Y. - config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -731,37 +581,6 @@ config ETRAX_PB_CHANGEABLE_BITS Bit set = changeable. You probably want 00 here. -config ETRAX_RTC - bool "Real Time Clock support" - depends on ETRAX_ARCH_V10 - help - Enables drivers for the Real-Time Clock battery-backed chips on - some products. The kernel reads the time when booting, and - the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a - rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc - device, major 121. You can check the time with cat /proc/rtc, but - normal time reading should be done using libc function time and - friends. - -choice - prompt "RTC chip" - depends on ETRAX_RTC - default ETRAX_DS1302 - -config ETRAX_DS1302 - bool "DS1302" - help - Enables the driver for the DS1302 Real-Time Clock battery-backed - chip on some products. - -config ETRAX_PCF8563 - bool "PCF8563" - help - Enables the driver for the PCF8563 Real-Time Clock battery-backed - chip on some products. - -endchoice - config ETRAX_DS1302_RST_ON_GENERIC_PORT bool "DS1302 RST on Generic Port" depends on ETRAX_DS1302 diff --git a/arch/cris/arch-v10/drivers/Makefile b/arch/cris/arch-v10/drivers/Makefile index 20258e36f38..44bf2e88c26 100644 --- a/arch/cris/arch-v10/drivers/Makefile +++ b/arch/cris/arch-v10/drivers/Makefile @@ -2,11 +2,11 @@ # Makefile for Etrax-specific drivers # -obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o -obj-$(CONFIG_ETRAX_I2C) += i2c.o -obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o -obj-$(CONFIG_ETRAX_GPIO) += gpio.o -obj-$(CONFIG_ETRAX_DS1302) += ds1302.o +obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o +obj-$(CONFIG_ETRAX_I2C) += i2c.o +obj-$(CONFIG_ETRAX_I2C_EEPROM) += eeprom.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o +obj-$(CONFIG_ETRAX_DS1302) += ds1302.o obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o - +obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o diff --git a/arch/cris/arch-v10/drivers/axisflashmap.c b/arch/cris/arch-v10/drivers/axisflashmap.c index ea3cf2e39a1..b3bdda93ffe 100644 --- a/arch/cris/arch-v10/drivers/axisflashmap.c +++ b/arch/cris/arch-v10/drivers/axisflashmap.c @@ -10,129 +10,6 @@ * tells us what other partitions to define. If there isn't, we use a default * partition split defined below. * - * $Log: axisflashmap.c,v $ - * Revision 1.11 2004/11/15 10:27:14 starvik - * Corrected typo (Thanks to Milton Miller <miltonm@bga.com>). - * - * Revision 1.10 2004/08/16 12:37:22 starvik - * Merge of Linux 2.6.8 - * - * Revision 1.8 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.6 2003/07/04 08:27:37 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2002/12/11 13:13:57 starvik - * Added arch/ to v10 specific includes - * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) - * - * Revision 1.4 2002/11/20 11:56:10 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.3 2002/11/13 14:54:13 starvik - * Copied from linux 2.4 - * - * Revision 1.28 2002/10/01 08:08:43 jonashg - * The first partition ends at the start of the partition table. - * - * Revision 1.27 2002/08/21 09:23:13 jonashg - * Speling. - * - * Revision 1.26 2002/08/21 08:35:20 jonashg - * Cosmetic change to printouts. - * - * Revision 1.25 2002/08/21 08:15:42 jonashg - * Made it compile even without CONFIG_MTD_CONCAT defined. - * - * Revision 1.24 2002/08/20 13:12:35 jonashg - * * New approach to probing. Probe cse0 and cse1 separately and (mtd)concat - * the results. - * * Removed compile time tests concerning how the mtdram driver has been - * configured. The user will know about the misconfiguration at runtime - * instead. (The old approach made it impossible to use mtdram for anything - * else than RAM boot). - * - * Revision 1.23 2002/05/13 12:12:28 johana - * Allow compile without CONFIG_MTD_MTDRAM but warn at compiletime and - * be informative at runtime. - * - * Revision 1.22 2002/05/13 10:24:44 johana - * Added #if checks on MTDRAM CONFIG - * - * Revision 1.21 2002/05/06 16:05:20 johana - * Removed debug printout. - * - * Revision 1.20 2002/05/06 16:03:00 johana - * No more cramfs as root hack in generic code. - * It's handled by axisflashmap using mtdram. - * - * Revision 1.19 2002/03/15 17:10:28 bjornw - * Changed comment about cached access since we changed this before - * - * Revision 1.18 2002/03/05 17:06:15 jonashg - * Try amd_flash probe before cfi_probe since amd_flash driver can handle two - * (or more) flash chips of different model and the cfi driver cannot. - * - * Revision 1.17 2001/11/12 19:42:38 pkj - * Fixed compiler warnings. - * - * Revision 1.16 2001/11/08 11:18:58 jonashg - * Always read from uncached address to avoid problems with flushing - * cachelines after write and MTD-erase. No performance loss have been - * seen yet. - * - * Revision 1.15 2001/10/19 12:41:04 jonashg - * Name of probe has changed in MTD. - * - * Revision 1.14 2001/09/21 07:14:10 jonashg - * Made root filesystem (cramfs) use mtdblock driver when booting from flash. - * - * Revision 1.13 2001/08/15 13:57:35 jonashg - * Entire MTD updated to the linux 2.4.7 version. - * - * Revision 1.12 2001/06/11 09:50:30 jonashg - * Oops, 2MB is 0x200000 bytes. - * - * Revision 1.11 2001/06/08 11:39:44 jonashg - * Changed sizes and offsets in axis_default_partitions to use - * CONFIG_ETRAX_PTABLE_SECTOR. - * - * Revision 1.10 2001/05/29 09:42:03 jonashg - * Use macro for end marker length instead of sizeof. - * - * Revision 1.9 2001/05/29 08:52:52 jonashg - * Gave names to the magic fours (size of the ptable end marker). - * - * Revision 1.8 2001/05/28 15:36:20 jonashg - * * Removed old comment about ptable location in flash (it's a CONFIG_ option). - * * Variable ptable was initialized twice to the same value. - * - * Revision 1.7 2001/04/05 13:41:46 markusl - * Updated according to review remarks - * - * Revision 1.6 2001/03/07 09:21:21 bjornw - * No need to waste .data - * - * Revision 1.5 2001/03/06 16:27:01 jonashg - * Probe the entire flash area for flash devices. - * - * Revision 1.4 2001/02/23 12:47:15 bjornw - * Uncached flash in LOW_MAP moved from 0xe to 0x8 - * - * Revision 1.3 2001/02/16 12:11:45 jonashg - * MTD driver amd_flash is now included in MTD CVS repository. - * (It's now in drivers/mtd). - * - * Revision 1.2 2001/02/09 11:12:22 jonashg - * Support for AMD compatible non-CFI flash chips. - * Only tested with Toshiba TC58FVT160 so far. - * - * Revision 1.1 2001/01/12 17:01:18 bjornw - * * Added axisflashmap.c, a physical mapping for MTD that reads and understands - * Axis partition-table format. - * - * */ #include <linux/module.h> @@ -235,7 +112,7 @@ static struct map_info map_cse1 = { }; /* If no partition-table was found, we use this default-set. */ -#define MAX_PARTITIONS 7 +#define MAX_PARTITIONS 7 #define NUM_DEFAULT_PARTITIONS 3 /* @@ -300,6 +177,15 @@ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { }, }; +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -316,15 +202,14 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) mtd_cs = do_map_probe("cfi_probe", map_cs); #endif #ifdef CONFIG_MTD_JEDECPROBE - if (!mtd_cs) { + if (!mtd_cs) mtd_cs = do_map_probe("jedec_probe", map_cs); - } #endif return mtd_cs; } -/* +/* * Probe each chip select individually for flash chips. If there are chips on * both cse0 and cse1, the mtd_info structs will be concatenated to one struct * so that MTD partitions can cross chip boundries. @@ -351,7 +236,7 @@ static struct mtd_info *flash_probe(void) if (mtd_cse0 && mtd_cse1) { #ifdef CONFIG_MTD_CONCAT struct mtd_info *mtds[] = { mtd_cse0, mtd_cse1 }; - + /* Since the concatenation layer adds a small overhead we * could try to figure out if the chips in cse0 and cse1 are * identical and reprobe the whole cse0+cse1 window. But since @@ -372,7 +257,7 @@ static struct mtd_info *flash_probe(void) /* The best we can do now is to only use what we found * at cse0. - */ + */ mtd_cse = mtd_cse0; map_destroy(mtd_cse1); } @@ -395,7 +280,7 @@ static int __init init_axis_flash(void) struct partitiontable_head *ptable_head = NULL; struct partitiontable_entry *ptable; int use_default_ptable = 1; /* Until proven otherwise. */ - const char *pmsg = " /dev/flash%d at 0x%08x, size 0x%08x\n"; + const char pmsg[] = " /dev/flash%d at 0x%08x, size 0x%08x\n"; if (!(mymtd = flash_probe())) { /* There's no reason to use this module if no flash chip can @@ -435,7 +320,7 @@ static int __init init_axis_flash(void) unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR; unsigned char *p; unsigned long csum = 0; - + ptable = (struct partitiontable_entry *) ((unsigned long)ptable_head + sizeof(*ptable_head)); @@ -490,6 +375,16 @@ static int __init init_axis_flash(void) pidx++; } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (mymtd) { + main_partition.size = mymtd->size; + err = add_mtd_partitions(mymtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); + } +#endif + if (mymtd) { if (use_default_ptable) { printk(KERN_INFO " Using default partition table.\n"); @@ -499,9 +394,8 @@ static int __init init_axis_flash(void) err = add_mtd_partitions(mymtd, axis_partitions, pidx); } - if (err) { + if (err) panic("axisflashmap could not add MTD partitions!\n"); - } } if (!romfs_in_flash) { @@ -515,25 +409,24 @@ static int __init init_axis_flash(void) #else struct mtd_info *mtd_ram; - mtd_ram = kmalloc(sizeof(struct mtd_info), - GFP_KERNEL); - if (!mtd_ram) { + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) panic("axisflashmap couldn't allocate memory for " "mtd_info!\n"); - } printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, romfs_start, romfs_length); - - err = mtdram_init_device(mtd_ram, (void*)romfs_start, - romfs_length, "romfs"); - if (err) { + printk(pmsg, pidx, (unsigned)romfs_start, + (unsigned)romfs_length); + + err = mtdram_init_device(mtd_ram, + (void *)romfs_start, + romfs_length, + "romfs"); + if (err) panic("axisflashmap could not initialize MTD RAM " "device!\n"); - } #endif } - return err; } diff --git a/arch/cris/arch-v10/drivers/ds1302.c b/arch/cris/arch-v10/drivers/ds1302.c index 1d1936a1813..c9aa3904be0 100644 --- a/arch/cris/arch-v10/drivers/ds1302.c +++ b/arch/cris/arch-v10/drivers/ds1302.c @@ -333,7 +333,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, ds1302_writereg(RTC_TRICKLECHARGER, tcs_val); return 0; } - case RTC_VLOW_RD: + case RTC_VL_READ: { /* TODO: * Implement voltage low detection support @@ -342,7 +342,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, " is not supported\n"); return 0; } - case RTC_VLOW_SET: + case RTC_VL_CLR: { /* TODO: * Nothing to do since Voltage Low detection is not supported diff --git a/arch/cris/arch-v10/drivers/eeprom.c b/arch/cris/arch-v10/drivers/eeprom.c index be35a70798a..f1cac9dc75b 100644 --- a/arch/cris/arch-v10/drivers/eeprom.c +++ b/arch/cris/arch-v10/drivers/eeprom.c @@ -19,77 +19,6 @@ *! Sep 03 1999 Edgar Iglesias Added bail-out stuff if we get interrupted *! in the spin-lock. *! -*! $Log: eeprom.c,v $ -*! Revision 1.12 2005/06/19 17:06:46 starvik -*! Merge of Linux 2.6.12. -*! -*! Revision 1.11 2005/01/26 07:14:46 starvik -*! Applied diff from kernel janitors (Nish Aravamudan). -*! -*! Revision 1.10 2003/09/11 07:29:48 starvik -*! Merge of Linux 2.6.0-test5 -*! -*! Revision 1.9 2003/07/04 08:27:37 starvik -*! Merge of Linux 2.5.74 -*! -*! Revision 1.8 2003/04/09 05:20:47 starvik -*! Merge of Linux 2.5.67 -*! -*! Revision 1.6 2003/02/10 07:19:28 starvik -*! Removed misplaced ; -*! -*! Revision 1.5 2002/12/11 13:13:57 starvik -*! Added arch/ to v10 specific includes -*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) -*! -*! Revision 1.4 2002/11/20 11:56:10 starvik -*! Merge of Linux 2.5.48 -*! -*! Revision 1.3 2002/11/18 13:16:06 starvik -*! Linux 2.5 port of latest 2.4 drivers -*! -*! Revision 1.8 2001/06/15 13:24:29 jonashg -*! * Added verification of pointers from userspace in read and write. -*! * Made busy counter volatile. -*! * Added define for initial write delay. -*! * Removed warnings by using loff_t instead of unsigned long. -*! -*! Revision 1.7 2001/06/14 15:26:54 jonashg -*! Removed test because condition is always true. -*! -*! Revision 1.6 2001/06/14 15:18:20 jonashg -*! Kb -> kB (makes quite a difference if you don't know if you have 2k or 16k). -*! -*! Revision 1.5 2001/06/14 14:39:51 jonashg -*! Forgot to use name when registering the driver. -*! -*! Revision 1.4 2001/06/14 14:35:47 jonashg -*! * Gave driver a name and used it in printk's. -*! * Cleanup. -*! -*! Revision 1.3 2001/03/19 16:04:46 markusl -*! Fixed init of fops struct -*! -*! Revision 1.2 2001/03/19 10:35:07 markusl -*! 2.4 port of eeprom driver -*! -*! Revision 1.8 2000/05/18 10:42:25 edgar -*! Make sure to end write cycle on _every_ write -*! -*! Revision 1.7 2000/01/17 17:41:01 johana -*! Adjusted probing and return -ENOSPC when writing outside EEPROM -*! -*! Revision 1.6 2000/01/17 15:50:36 johana -*! Added adaptive timing adjustments and fixed autoprobing for 2k and 16k(?) -*! EEPROMs -*! -*! Revision 1.5 1999/09/03 15:07:37 edgar -*! Added bail-out check to the spinlock -*! -*! Revision 1.4 1999/09/03 12:11:17 bjornw -*! Proper atomicity (need to use spinlocks, not if's). users -> busy. -*! -*! *! (c) 1999 Axis Communications AB, Lund, Sweden *!*****************************************************************************/ @@ -103,10 +32,10 @@ #include <asm/uaccess.h> #include "i2c.h" -#define D(x) +#define D(x) /* If we should use adaptive timing or not: */ -//#define EEPROM_ADAPTIVE_TIMING +/* #define EEPROM_ADAPTIVE_TIMING */ #define EEPROM_MAJOR_NR 122 /* use a LOCAL/EXPERIMENTAL major for now */ #define EEPROM_MINOR_NR 0 diff --git a/arch/cris/arch-v10/drivers/gpio.c b/arch/cris/arch-v10/drivers/gpio.c index 0d347a70583..68a998bd106 100644 --- a/arch/cris/arch-v10/drivers/gpio.c +++ b/arch/cris/arch-v10/drivers/gpio.c @@ -1,138 +1,11 @@ -/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $ - * +/* * Etrax general port I/O device * - * Copyright (c) 1999, 2000, 2001, 2002 Axis Communications AB + * Copyright (c) 1999-2007 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G) - * - * $Log: gpio.c,v $ - * Revision 1.17 2005/06/19 17:06:46 starvik - * Merge of Linux 2.6.12. - * - * Revision 1.16 2005/03/07 13:02:29 starvik - * Protect driver global states with spinlock - * - * Revision 1.15 2005/01/05 06:08:55 starvik - * No need to do local_irq_disable after local_irq_save. - * - * Revision 1.14 2004/12/13 12:21:52 starvik - * Added I/O and DMA allocators from Linux 2.4 - * - * Revision 1.12 2004/08/24 07:19:59 starvik - * Whitespace cleanup - * - * Revision 1.11 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.9 2003/09/11 07:29:48 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.8 2003/07/04 08:27:37 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.7 2003/01/10 07:44:07 starvik - * init_ioremap is now called by kernel before drivers are initialized - * - * Revision 1.6 2002/12/11 13:13:57 starvik - * Added arch/ to v10 specific includes - * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) - * - * Revision 1.5 2002/11/20 11:56:11 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.4 2002/11/18 10:10:05 starvik - * Linux 2.5 port of latest gpio.c from Linux 2.4 - * - * Revision 1.20 2002/10/16 21:16:24 johana - * Added support for PA high level interrupt. - * That gives 2ms response time with iodtest for high levels and 2-12 ms - * response time on low levels if the check is not made in - * process.c:cpu_idle() as well. - * - * Revision 1.19 2002/10/14 18:27:33 johana - * Implemented alarm handling so select() now works. - * Latency is around 6-9 ms with a etrax_gpio_wake_up_check() in - * cpu_idle(). - * Otherwise I get 15-18 ms (same as doing the poll in userspace - - * but less overhead). - * TODO? Perhaps we should add the check in IMMEDIATE_BH (or whatever it - * is in 2.4) as well? - * TODO? Perhaps call request_irq()/free_irq() only when needed? - * Increased version to 2.5 - * - * Revision 1.18 2002/10/11 15:02:00 johana - * Mask inverted 8 bit value in setget_input(). - * - * Revision 1.17 2002/06/17 15:53:01 johana - * Added IO_READ_INBITS, IO_READ_OUTBITS, IO_SETGET_INPUT and IO_SETGET_OUTPUT - * that take a pointer as argument and thus can handle 32 bit ports (G) - * correctly. - * These should be used instead of IO_READBITS, IO_SETINPUT and IO_SETOUTPUT. - * (especially if Port G bit 31 is used) - * - * Revision 1.16 2002/06/17 09:59:51 johana - * Returning 32 bit values in the ioctl return value doesn't work if bit - * 31 is set (could happen for port G), so mask it of with 0x7FFFFFFF. - * A new set of ioctl's will be added. - * - * Revision 1.15 2002/05/06 13:19:13 johana - * IO_SETINPUT returns mask with bit set = inputs for PA and PB as well. - * - * Revision 1.14 2002/04/12 12:01:53 johana - * Use global r_port_g_data_shadow. - * Moved gpio_init_port_g() closer to gpio_init() and marked it __init. - * - * Revision 1.13 2002/04/10 12:03:55 johana - * Added support for port G /dev/gpiog (minor 3). - * Changed indentation on switch cases. - * Fixed other spaces to tabs. - * - * Revision 1.12 2001/11/12 19:42:15 pkj - * * Corrected return values from gpio_leds_ioctl(). - * * Fixed compiler warnings. - * - * Revision 1.11 2001/10/30 14:39:12 johana - * Added D() around gpio_write printk. - * - * Revision 1.10 2001/10/25 10:24:42 johana - * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast - * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB - * from ~60 seconds to 4 seconds). - * Added save_flags/cli/restore_flags in ioctl. - * - * Revision 1.9 2001/05/04 14:16:07 matsfg - * Corrected spelling error - * - * Revision 1.8 2001/04/27 13:55:26 matsfg - * Moved initioremap. - * Turns off all LEDS on init. - * Added support for shutdown and powerbutton. - * - * Revision 1.7 2001/04/04 13:30:08 matsfg - * Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping - * - * Revision 1.6 2001/03/26 16:03:06 bjornw - * Needs linux/config.h - * - * Revision 1.5 2001/03/26 14:22:03 bjornw - * Namechange of some config options - * - * Revision 1.4 2001/02/27 13:52:48 bjornw - * malloc.h -> slab.h - * - * Revision 1.3 2001/01/24 15:06:48 bjornw - * gpio_wq correct type - * - * Revision 1.2 2001/01/18 16:07:30 bjornw - * 2.4 port - * - * Revision 1.1 2001/01/18 15:55:16 bjornw - * Verbatim copy of etraxgpio.c from elinux 2.0 added - * - * */ @@ -165,7 +38,7 @@ static int dp_cnt; #else #define DP(x) #endif - + static char gpio_name[] = "etrax gpio"; #if 0 @@ -173,9 +46,9 @@ static wait_queue_head_t *gpio_wq; #endif static int gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off); + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off); static int gpio_open(struct inode *inode, struct file *filp); static int gpio_release(struct inode *inode, struct file *filp); static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); @@ -201,22 +74,22 @@ struct gpio_private { /* linked list of alarms to check for */ -static struct gpio_private *alarmlist = 0; +static struct gpio_private *alarmlist; -static int gpio_some_alarms = 0; /* Set if someone uses alarm */ -static unsigned long gpio_pa_irq_enabled_mask = 0; +static int gpio_some_alarms; /* Set if someone uses alarm */ +static unsigned long gpio_pa_irq_enabled_mask; static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */ /* Port A and B use 8 bit access, but Port G is 32 bit */ #define NUM_PORTS (GPIO_MINOR_B+1) -static volatile unsigned char *ports[NUM_PORTS] = { - R_PORT_PA_DATA, +static volatile unsigned char *ports[NUM_PORTS] = { + R_PORT_PA_DATA, R_PORT_PB_DATA, }; static volatile unsigned char *shads[NUM_PORTS] = { - &port_pa_data_shadow, + &port_pa_data_shadow, &port_pb_data_shadow }; @@ -236,29 +109,29 @@ static volatile unsigned char *shads[NUM_PORTS] = { #endif -static unsigned char changeable_dir[NUM_PORTS] = { +static unsigned char changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_DIR, - CONFIG_ETRAX_PB_CHANGEABLE_DIR + CONFIG_ETRAX_PB_CHANGEABLE_DIR }; -static unsigned char changeable_bits[NUM_PORTS] = { +static unsigned char changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PA_CHANGEABLE_BITS, - CONFIG_ETRAX_PB_CHANGEABLE_BITS + CONFIG_ETRAX_PB_CHANGEABLE_BITS }; -static volatile unsigned char *dir[NUM_PORTS] = { - R_PORT_PA_DIR, - R_PORT_PB_DIR +static volatile unsigned char *dir[NUM_PORTS] = { + R_PORT_PA_DIR, + R_PORT_PB_DIR }; static volatile unsigned char *dir_shadow[NUM_PORTS] = { - &port_pa_dir_shadow, - &port_pb_dir_shadow + &port_pa_dir_shadow, + &port_pb_dir_shadow }; /* All bits in port g that can change dir. */ static const unsigned long int changeable_dir_g_mask = 0x01FFFF01; -/* Port G is 32 bit, handle it special, some bits are both inputs +/* Port G is 32 bit, handle it special, some bits are both inputs and outputs at the same time, only some of the bits can change direction and some of them in groups of 8 bit. */ static unsigned long changeable_dir_g; @@ -269,18 +142,17 @@ static unsigned long dir_g_shadow; /* 1=output */ #define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) - -static unsigned int -gpio_poll(struct file *file, - poll_table *wait) +static unsigned int gpio_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; - struct gpio_private *priv = (struct gpio_private *)file->private_data; + struct gpio_private *priv = file->private_data; unsigned long data; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + poll_wait(file, &priv->alarm_wq, wait); if (priv->minor == GPIO_MINOR_A) { - unsigned long flags; unsigned long tmp; data = *R_PORT_PA_DATA; /* PA has support for high level interrupt - @@ -288,27 +160,25 @@ gpio_poll(struct file *file, */ tmp = ~data & priv->highalarm & 0xFF; tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); - local_irq_save(flags); + gpio_pa_irq_enabled_mask |= tmp; *R_IRQ_MASK1_SET = tmp; - local_irq_restore(flags); - } else if (priv->minor == GPIO_MINOR_B) data = *R_PORT_PB_DATA; else if (priv->minor == GPIO_MINOR_G) data = *R_PORT_G_DATA; else { - spin_unlock(&gpio_lock); - return 0; + mask = 0; + goto out; } - + if ((data & priv->highalarm) || (~data & priv->lowalarm)) { mask = POLLIN|POLLRDNORM; } - spin_unlock(&gpio_lock); - +out: + spin_unlock_irqrestore(&gpio_lock, flags); DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); return mask; @@ -316,16 +186,19 @@ gpio_poll(struct file *file, int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; int ret = 0; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + priv = alarmlist; while (priv) { - if (USE_PORTS(priv)) { + if (USE_PORTS(priv)) data = *priv->port; - } else if (priv->minor == GPIO_MINOR_G) { + else if (priv->minor == GPIO_MINOR_G) data = *R_PORT_G_DATA; - } + if ((data & priv->highalarm) || (~data & priv->lowalarm)) { DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); @@ -334,12 +207,12 @@ int etrax_gpio_wake_up_check(void) } priv = priv->next; } - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return ret; } static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_poll_timer_interrupt(int irq, void *dev_id) { if (gpio_some_alarms) { etrax_gpio_wake_up_check(); @@ -349,10 +222,13 @@ gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_interrupt(int irq, void *dev_id) { unsigned long tmp; - spin_lock(&gpio_lock); + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + /* Find what PA interrupts are active */ tmp = (*R_IRQ_READ1); @@ -363,75 +239,70 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) *R_IRQ_MASK1_CLR = tmp; gpio_pa_irq_enabled_mask &= ~tmp; - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } + return IRQ_NONE; } +static void gpio_write_bit(struct gpio_private *priv, + unsigned char data, int bit) +{ + *priv->port = *priv->shadow &= ~(priv->clk_mask); + if (data & 1 << bit) + *priv->port = *priv->shadow |= priv->data_mask; + else + *priv->port = *priv->shadow &= ~(priv->data_mask); + + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + *priv->port = *priv->shadow |= priv->clk_mask; +} -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off) +static void gpio_write_byte(struct gpio_private *priv, unsigned char data) { - struct gpio_private *priv = (struct gpio_private *)file->private_data; - unsigned char data, clk_mask, data_mask, write_msb; - unsigned long flags; + int i; - spin_lock(&gpio_lock); + if (priv->write_msb) + for (i = 7; i >= 0; i--) + gpio_write_bit(priv, data, i); + else + for (i = 0; i <= 7; i++) + gpio_write_bit(priv, data, i); +} +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_private *priv = file->private_data; + unsigned long flags; ssize_t retval = count; - if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) { - retval = -EFAULT; - goto out; - } - - if (!access_ok(VERIFY_READ, buf, count)) { - retval = -EFAULT; - goto out; - } - clk_mask = priv->clk_mask; - data_mask = priv->data_mask; + + if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) + return -EFAULT; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + spin_lock_irqsave(&gpio_lock, flags); + /* It must have been configured using the IO_CFG_WRITE_MODE */ /* Perhaps a better error code? */ - if (clk_mask == 0 || data_mask == 0) { + if (priv->clk_mask == 0 || priv->data_mask == 0) { retval = -EPERM; goto out; } - write_msb = priv->write_msb; - D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); - while (count--) { - int i; - data = *buf++; - if (priv->write_msb) { - for (i = 7; i >= 0;i--) { - local_irq_save(flags); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - local_irq_restore(flags); - } - } else { - for (i = 0; i <= 7;i++) { - local_irq_save(flags); - *priv->port = *priv->shadow &= ~clk_mask; - if (data & 1<<i) - *priv->port = *priv->shadow |= data_mask; - else - *priv->port = *priv->shadow &= ~data_mask; - /* For FPGA: min 5.0ns (DCC) before CCLK high */ - *priv->port = *priv->shadow |= clk_mask; - local_irq_restore(flags); - } - } - } + + D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X " + "clk 0x%02X msb: %i\n", + count, priv->data_mask, priv->clk_mask, priv->write_msb)); + + while (count--) + gpio_write_byte(priv, *buf++); + out: - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return retval; } @@ -442,22 +313,20 @@ gpio_open(struct inode *inode, struct file *filp) { struct gpio_private *priv; int p = iminor(inode); + unsigned long flags; if (p > GPIO_MINOR_LAST) return -EINVAL; - priv = kmalloc(sizeof(struct gpio_private), - GFP_KERNEL); + priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; if (USE_PORTS(priv)) { /* A and B */ priv->port = ports[p]; priv->shadow = shads[p]; @@ -480,7 +349,13 @@ gpio_open(struct inode *inode, struct file *filp) priv->data_mask = 0; init_waitqueue_head(&priv->alarm_wq); - filp->private_data = (void *)priv; + filp->private_data = priv; + + /* link it into our alarmlist */ + spin_lock_irqsave(&gpio_lock, flags); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irqrestore(&gpio_lock, flags); return 0; } @@ -490,11 +365,12 @@ gpio_release(struct inode *inode, struct file *filp) { struct gpio_private *p; struct gpio_private *todel; + unsigned long flags; - spin_lock(&gpio_lock); + spin_lock_irqsave(&gpio_lock, flags); - p = alarmlist; - todel = (struct gpio_private *)filp->private_data; + p = alarmlist; + todel = filp->private_data; /* unlink from alarmlist and free the private structure */ @@ -512,123 +388,114 @@ gpio_release(struct inode *inode, struct file *filp) while (p) { if (p->highalarm | p->lowalarm) { gpio_some_alarms = 1; - spin_unlock(&gpio_lock); - return 0; + goto out; } p = p->next; } gpio_some_alarms = 0; - spin_unlock(&gpio_lock); +out: + spin_unlock_irqrestore(&gpio_lock, flags); return 0; } -/* Main device API. ioctl's to read/set/clear bits, as well as to +/* Main device API. ioctl's to read/set/clear bits, as well as to * set alarms to wait for using a subsequent select(). */ - unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) { - /* Set direction 0=unchanged 1=input, - * return mask with 1=input - */ - unsigned long flags; + /* Set direction 0=unchanged 1=input, + * return mask with 1=input */ if (USE_PORTS(priv)) { - local_irq_save(flags); - *priv->dir = *priv->dir_shadow &= + *priv->dir = *priv->dir_shadow &= ~((unsigned char)arg & priv->changeable_dir); - local_irq_restore(flags); return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ - } else if (priv->minor == GPIO_MINOR_G) { - /* We must fiddle with R_GEN_CONFIG to change dir */ - local_irq_save(flags); - if (((arg & dir_g_in_bits) != arg) && - (arg & changeable_dir_g)) { - arg &= changeable_dir_g; - /* Clear bits in genconfig to set to input */ - if (arg & (1<<0)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir); - dir_g_in_bits |= (1<<0); - dir_g_out_bits &= ~(1<<0); - } - if ((arg & 0x0000FF00) == 0x0000FF00) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir); - dir_g_in_bits |= 0x0000FF00; - dir_g_out_bits &= ~0x0000FF00; - } - if ((arg & 0x00FF0000) == 0x00FF0000) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir); - dir_g_in_bits |= 0x00FF0000; - dir_g_out_bits &= ~0x00FF0000; - } - if (arg & (1<<24)) { - genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir); - dir_g_in_bits |= (1<<24); - dir_g_out_bits &= ~(1<<24); - } - D(printk(KERN_INFO "gpio: SETINPUT on port G set " - "genconfig to 0x%08lX " - "in_bits: 0x%08lX " - "out_bits: 0x%08lX\n", - (unsigned long)genconfig_shadow, - dir_g_in_bits, dir_g_out_bits)); - *R_GEN_CONFIG = genconfig_shadow; - /* Must be a >120 ns delay before writing this again */ - + } + + if (priv->minor != GPIO_MINOR_G) + return 0; + + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_in_bits) != arg) && + (arg & changeable_dir_g)) { + arg &= changeable_dir_g; + /* Clear bits in genconfig to set to input */ + if (arg & (1<<0)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir); + dir_g_in_bits |= (1<<0); + dir_g_out_bits &= ~(1<<0); } - local_irq_restore(flags); - return dir_g_in_bits; + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir); + dir_g_in_bits |= 0x0000FF00; + dir_g_out_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir); + dir_g_in_bits |= 0x00FF0000; + dir_g_out_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir); + dir_g_in_bits |= (1<<24); + dir_g_out_bits &= ~(1<<24); + } + D(printk(KERN_DEBUG "gpio: SETINPUT on port G set " + "genconfig to 0x%08lX " + "in_bits: 0x%08lX " + "out_bits: 0x%08lX\n", + (unsigned long)genconfig_shadow, + dir_g_in_bits, dir_g_out_bits)); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ + } - return 0; + return dir_g_in_bits; } /* setget_input */ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) { - unsigned long flags; if (USE_PORTS(priv)) { - local_irq_save(flags); - *priv->dir = *priv->dir_shadow |= - ((unsigned char)arg & priv->changeable_dir); - local_irq_restore(flags); + *priv->dir = *priv->dir_shadow |= + ((unsigned char)arg & priv->changeable_dir); return *priv->dir_shadow; - } else if (priv->minor == GPIO_MINOR_G) { - /* We must fiddle with R_GEN_CONFIG to change dir */ - local_irq_save(flags); - if (((arg & dir_g_out_bits) != arg) && - (arg & changeable_dir_g)) { - /* Set bits in genconfig to set to output */ - if (arg & (1<<0)) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir); - dir_g_out_bits |= (1<<0); - dir_g_in_bits &= ~(1<<0); - } - if ((arg & 0x0000FF00) == 0x0000FF00) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir); - dir_g_out_bits |= 0x0000FF00; - dir_g_in_bits &= ~0x0000FF00; - } - if ((arg & 0x00FF0000) == 0x00FF0000) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir); - dir_g_out_bits |= 0x00FF0000; - dir_g_in_bits &= ~0x00FF0000; - } - if (arg & (1<<24)) { - genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir); - dir_g_out_bits |= (1<<24); - dir_g_in_bits &= ~(1<<24); - } - D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " - "genconfig to 0x%08lX " - "in_bits: 0x%08lX " - "out_bits: 0x%08lX\n", - (unsigned long)genconfig_shadow, - dir_g_in_bits, dir_g_out_bits)); - *R_GEN_CONFIG = genconfig_shadow; - /* Must be a >120 ns delay before writing this again */ + } + if (priv->minor != GPIO_MINOR_G) + return 0; + + /* We must fiddle with R_GEN_CONFIG to change dir */ + if (((arg & dir_g_out_bits) != arg) && + (arg & changeable_dir_g)) { + /* Set bits in genconfig to set to output */ + if (arg & (1<<0)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir); + dir_g_out_bits |= (1<<0); + dir_g_in_bits &= ~(1<<0); } - local_irq_restore(flags); - return dir_g_out_bits & 0x7FFFFFFF; + if ((arg & 0x0000FF00) == 0x0000FF00) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir); + dir_g_out_bits |= 0x0000FF00; + dir_g_in_bits &= ~0x0000FF00; + } + if ((arg & 0x00FF0000) == 0x00FF0000) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir); + dir_g_out_bits |= 0x00FF0000; + dir_g_in_bits &= ~0x00FF0000; + } + if (arg & (1<<24)) { + genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir); + dir_g_out_bits |= (1<<24); + dir_g_in_bits &= ~(1<<24); + } + D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " + "genconfig to 0x%08lX " + "in_bits: 0x%08lX " + "out_bits: 0x%08lX\n", + (unsigned long)genconfig_shadow, + dir_g_in_bits, dir_g_out_bits)); + *R_GEN_CONFIG = genconfig_shadow; + /* Must be a >120 ns delay before writing this again */ } - return 0; + return dir_g_out_bits & 0x7FFFFFFF; } /* setget_output */ static int @@ -642,12 +509,11 @@ gpio_ioctl(struct inode *inode, struct file *file, unsigned long val; int ret = 0; - struct gpio_private *priv = (struct gpio_private *)file->private_data; - if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + struct gpio_private *priv = file->private_data; + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) return -EINVAL; - } - spin_lock(&gpio_lock); + spin_lock_irqsave(&gpio_lock, flags); switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ @@ -659,7 +525,6 @@ gpio_ioctl(struct inode *inode, struct file *file, } break; case IO_SETBITS: - local_irq_save(flags); // set changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow |= @@ -667,10 +532,8 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); } - local_irq_restore(flags); break; case IO_CLRBITS: - local_irq_save(flags); // clear changeable bits with a 1 in arg if (USE_PORTS(priv)) { *priv->port = *priv->shadow &= @@ -678,7 +541,6 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); } - local_irq_restore(flags); break; case IO_HIGHALARM: // set alarm when bits with 1 in arg go high @@ -698,6 +560,8 @@ gpio_ioctl(struct inode *inode, struct file *file, /* Must update gpio_some_alarms */ struct gpio_private *p = alarmlist; int some_alarms; + spin_lock_irq(&gpio_lock); + p = alarmlist; some_alarms = 0; while (p) { if (p->highalarm | p->lowalarm) { @@ -707,6 +571,7 @@ gpio_ioctl(struct inode *inode, struct file *file, p = p->next; } gpio_some_alarms = some_alarms; + spin_unlock_irq(&gpio_lock); } break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ @@ -766,7 +631,7 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { val = *R_PORT_G_DATA; } - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_READ_OUTBITS: @@ -776,33 +641,32 @@ gpio_ioctl(struct inode *inode, struct file *file, } else if (priv->minor == GPIO_MINOR_G) { val = port_g_data_shadow; } - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { ret = -EFAULT; break; } val = setget_input(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; case IO_SETGET_OUTPUT: /* bits set in *arg is set to output, * *arg updated with current output pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) - { + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { ret = -EFAULT; break; } val = setget_output(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((void __user *)arg, &val, sizeof(val))) ret = -EFAULT; break; default: @@ -812,7 +676,7 @@ gpio_ioctl(struct inode *inode, struct file *file, ret = -EINVAL; } /* switch */ - spin_unlock(&gpio_lock); + spin_unlock_irqrestore(&gpio_lock, flags); return ret; } @@ -824,18 +688,18 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) switch (_IOC_NR(cmd)) { case IO_LEDACTIVE_SET: - green = ((unsigned char) arg) & 1; - red = (((unsigned char) arg) >> 1) & 1; - LED_ACTIVE_SET_G(green); - LED_ACTIVE_SET_R(red); + green = ((unsigned char)arg) & 1; + red = (((unsigned char)arg) >> 1) & 1; + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); break; case IO_LED_SETBIT: - LED_BIT_SET(arg); + CRIS_LED_BIT_SET(arg); break; case IO_LED_CLRBIT: - LED_BIT_CLR(arg); + CRIS_LED_BIT_CLR(arg); break; default: @@ -845,7 +709,7 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) return 0; } -const struct file_operations gpio_fops = { +static const struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, @@ -854,16 +718,18 @@ const struct file_operations gpio_fops = { .release = gpio_release, }; - -void ioif_watcher(const unsigned int gpio_in_available, - const unsigned int gpio_out_available, - const unsigned char pa_available, - const unsigned char pb_available) +static void ioif_watcher(const unsigned int gpio_in_available, + const unsigned int gpio_out_available, + const unsigned char pa_available, + const unsigned char pb_available) { unsigned long int flags; - D(printk("gpio.c: ioif_watcher called\n")); - D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n", - gpio_in_available, gpio_out_available, pa_available, pb_available)); + + D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n")); + D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x " + "PA: 0x%02x PB: 0x%02x\n", + gpio_in_available, gpio_out_available, + pa_available, pb_available)); spin_lock_irqsave(&gpio_lock, flags); @@ -872,7 +738,7 @@ void ioif_watcher(const unsigned int gpio_in_available, /* Initialise the dir_g_shadow etc. depending on genconfig */ /* 0=input 1=output */ - if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) + if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) dir_g_shadow |= (1 << 0); if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) dir_g_shadow |= 0x0000FF00; @@ -884,7 +750,8 @@ void ioif_watcher(const unsigned int gpio_in_available, changeable_dir_g = changeable_dir_g_mask; changeable_dir_g &= dir_g_out_bits; changeable_dir_g &= dir_g_in_bits; - /* Correct the bits that can change direction */ + + /* Correct the bits that can change direction */ dir_g_out_bits &= ~changeable_dir_g; dir_g_out_bits |= dir_g_shadow; dir_g_in_bits &= ~changeable_dir_g; @@ -892,7 +759,8 @@ void ioif_watcher(const unsigned int gpio_in_available, spin_unlock_irqrestore(&gpio_lock, flags); - printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n", + printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX " + "val: %08lX\n", dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", dir_g_shadow, changeable_dir_g); @@ -900,16 +768,12 @@ void ioif_watcher(const unsigned int gpio_in_available, /* main driver initialization routine, called from mem.c */ -static __init int -gpio_init(void) +static int __init gpio_init(void) { int res; #if defined (CONFIG_ETRAX_CSP0_LEDS) int i; #endif - printk("gpio init\n"); - - /* do the formalities */ res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); if (res < 0) { @@ -919,43 +783,45 @@ gpio_init(void) /* Clear all leds */ #if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) - LED_NETWORK_SET(0); - LED_ACTIVE_SET(0); - LED_DISK_READ(0); - LED_DISK_WRITE(0); + CRIS_LED_NETWORK_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); #if defined (CONFIG_ETRAX_CSP0_LEDS) - for (i = 0; i < 32; i++) { - LED_BIT_SET(i); - } + for (i = 0; i < 32; i++) + CRIS_LED_BIT_SET(i); #endif #endif /* The I/O interface allocation watcher will be called when * registering it. */ if (cris_io_interface_register_watcher(ioif_watcher)){ - printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n"); + printk(KERN_WARNING "gpio_init: Failed to install IO " + "if allocator watcher\n"); } - printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n"); + printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 " + "Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. - */ - if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) { + */ + res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name); + if (res) { printk(KERN_CRIT "err: timer0 irq for gpio\n"); + return res; } - if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) { + res = request_irq(PA_IRQ_NBR, gpio_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name); + if (res) printk(KERN_CRIT "err: PA irq for gpio\n"); - } - return res; } /* this makes sure that gpio_init is called during kernel boot */ - module_init(gpio_init); + diff --git a/arch/cris/arch-v10/drivers/i2c.c b/arch/cris/arch-v10/drivers/i2c.c index aca81ddaf60..d6d22067d0c 100644 --- a/arch/cris/arch-v10/drivers/i2c.c +++ b/arch/cris/arch-v10/drivers/i2c.c @@ -6,85 +6,9 @@ *! kernel modules (i2c_writereg/readreg) and from userspace using *! ioctl()'s *! -*! Nov 30 1998 Torbjorn Eliasson Initial version. -*! Bjorn Wesen Elinux kernel version. -*! Jan 14 2000 Johan Adolfsson Fixed PB shadow register stuff - -*! don't use PB_I2C if DS1302 uses same bits, -*! use PB. -*! $Log: i2c.c,v $ -*! Revision 1.13 2005/03/07 13:13:07 starvik -*! Added spinlocks to protect states etc -*! -*! Revision 1.12 2005/01/05 06:11:22 starvik -*! No need to do local_irq_disable after local_irq_save. -*! -*! Revision 1.11 2004/12/13 12:21:52 starvik -*! Added I/O and DMA allocators from Linux 2.4 -*! -*! Revision 1.9 2004/08/24 06:49:14 starvik -*! Whitespace cleanup -*! -*! Revision 1.8 2004/06/08 08:48:26 starvik -*! Removed unused code -*! -*! Revision 1.7 2004/05/28 09:26:59 starvik -*! Modified I2C initialization to work in 2.6. -*! -*! Revision 1.6 2004/05/14 07:58:03 starvik -*! Merge of changes from 2.4 -*! -*! Revision 1.4 2002/12/11 13:13:57 starvik -*! Added arch/ to v10 specific includes -*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer) -*! -*! Revision 1.3 2002/11/20 11:56:11 starvik -*! Merge of Linux 2.5.48 -*! -*! Revision 1.2 2002/11/18 13:16:06 starvik -*! Linux 2.5 port of latest 2.4 drivers -*! -*! Revision 1.9 2002/10/31 15:32:26 starvik -*! Update Port B register and shadow even when running with hardware support -*! to avoid glitches when reading bits -*! Never set direction to out in i2c_inbyte -*! Removed incorrect clock toggling at end of i2c_inbyte -*! -*! Revision 1.8 2002/08/13 06:31:53 starvik -*! Made SDA and SCL line configurable -*! Modified i2c_inbyte to work with PCF8563 -*! -*! Revision 1.7 2001/04/04 13:11:36 markusl -*! Updated according to review remarks -*! -*! Revision 1.6 2001/03/19 12:43:00 markusl -*! Made some symbols unstatic (used by the eeprom driver) -*! -*! Revision 1.5 2001/02/27 13:52:48 bjornw -*! malloc.h -> slab.h -*! -*! Revision 1.4 2001/02/15 07:17:40 starvik -*! Corrected usage if port_pb_i2c_shadow -*! -*! Revision 1.3 2001/01/26 17:55:13 bjornw -*! * Made I2C_USES_PB_NOT_PB_I2C a CONFIG option instead of assigning it -*! magically. Config.in needs to set it for the options that need it, like -*! Dallas 1302 support. Actually, it should be default since it screws up -*! the PB bits even if you don't use I2C.. -*! * Include linux/config.h to get the above -*! -*! Revision 1.2 2001/01/18 15:49:30 bjornw -*! 2.4 port of I2C including some cleanups (untested of course) -*! -*! Revision 1.1 2001/01/18 15:35:25 bjornw -*! Verbatim copy of the Etrax i2c driver, 2.0 elinux version -*! -*! -*! --------------------------------------------------------------------------- -*! -*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */ /****************** INCLUDE FILES SECTION ***********************************/ @@ -622,7 +546,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * last received byte needs to be nacked * instead of acked */ - i2c_sendack(); + i2c_sendnack(); /* * end sequence */ @@ -708,6 +632,7 @@ i2c_init(void) if (!first) { return res; } + first = 0; /* Setup and enable the Port B I2C interface */ diff --git a/arch/cris/arch-v10/drivers/pcf8563.c b/arch/cris/arch-v10/drivers/pcf8563.c index c263b8232db..52103d16dc6 100644 --- a/arch/cris/arch-v10/drivers/pcf8563.c +++ b/arch/cris/arch-v10/drivers/pcf8563.c @@ -8,14 +8,13 @@ * low detector are also provided. All address and data are transferred * serially via two-line bidirectional I2C-bus. Maximum bus speed is * 400 kbits/s. The built-in word address register is incremented - * automatically after each written or read bute. + * automatically after each written or read byte. * - * Copyright (c) 2002, Axis Communications AB + * Copyright (c) 2002-2007, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg <tobiasa@axis.com>. * - * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $ */ #include <linux/module.h> @@ -27,19 +26,19 @@ #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/bcd.h> -#include <linux/capability.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> -#include <asm/arch/svinto.h> #include <asm/rtc.h> + #include "i2c.h" -#define PCF8563_MAJOR 121 /* Local major number. */ -#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ +#define PCF8563_MAJOR 121 /* Local major number. */ +#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.11 $" +#define DRIVER_VERSION "$Revision: 1.24 $" /* I2C bus slave registers. */ #define RTC_I2C_READ 0xa3 @@ -49,71 +48,88 @@ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) -static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */ - +static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low; + static const struct file_operations pcf8563_fops = { .owner = THIS_MODULE, .ioctl = pcf8563_ioctl, }; unsigned char -pcf8563_readreg(int reg) +pcf8563_readreg(int reg) { - unsigned char res = i2c_readreg(RTC_I2C_READ, reg); - - /* The PCF8563 does not return 0 for unimplemented bits */ - switch(reg) - { - case RTC_SECONDS: - case RTC_MINUTES: - res &= 0x7f; - break; - case RTC_HOURS: - case RTC_DAY_OF_MONTH: - res &= 0x3f; - break; - case RTC_MONTH: - res = (res & 0x1f) - 1; /* PCF8563 returns month in range 1-12 */ - break; + unsigned char res = rtc_read(reg); + + /* The PCF8563 does not return 0 for unimplemented bits. */ + switch (reg) { + case RTC_SECONDS: + case RTC_MINUTES: + res &= 0x7F; + break; + case RTC_HOURS: + case RTC_DAY_OF_MONTH: + res &= 0x3F; + break; + case RTC_WEEKDAY: + res &= 0x07; + break; + case RTC_MONTH: + res &= 0x1F; + break; + case RTC_CONTROL1: + res &= 0xA8; + break; + case RTC_CONTROL2: + res &= 0x1F; + break; + case RTC_CLOCKOUT_FREQ: + case RTC_TIMER_CONTROL: + res &= 0x83; + break; } return res; } void -pcf8563_writereg(int reg, unsigned char val) +pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } void get_rtc_time(struct rtc_time *tm) { - tm->tm_sec = rtc_read(RTC_SECONDS); - tm->tm_min = rtc_read(RTC_MINUTES); + tm->tm_sec = rtc_read(RTC_SECONDS); + tm->tm_min = rtc_read(RTC_MINUTES); tm->tm_hour = rtc_read(RTC_HOURS); tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); - tm->tm_mon = rtc_read(RTC_MONTH); + tm->tm_wday = rtc_read(RTC_WEEKDAY); + tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); + if (tm->tm_sec & 0x80) { + printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); - tm->tm_sec &= 0x7f; - tm->tm_min &= 0x7f; - tm->tm_hour &= 0x3f; - tm->tm_mday &= 0x3f; - tm->tm_mon &= 0x1f; + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_sec &= 0x7F; + tm->tm_min &= 0x7F; + tm->tm_hour &= 0x3F; + tm->tm_mday &= 0x3F; + tm->tm_wday &= 0x07; /* Not coded in BCD. */ + tm->tm_mon &= 0x1F; BCD_TO_BIN(tm->tm_sec); BCD_TO_BIN(tm->tm_min); @@ -126,17 +142,24 @@ get_rtc_time(struct rtc_time *tm) int __init pcf8563_init(void) { - int ret; - - if ((ret = i2c_init())) { - printk(KERN_CRIT "pcf8563_init: failed to init i2c\n"); - return ret; + static int res; + static int first = 1; + + if (!first) + return res; + first = 0; + + /* Initiate the i2c protocol. */ + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; } /* * First of all we need to reset the chip. This is done by - * clearing control1, control2 and clk freq, clear the - * Voltage Low bit, and resetting all alarms. + * clearing control1, control2 and clk freq and resetting + * all alarms. */ if (rtc_write(RTC_CONTROL1, 0x00) < 0) goto err; @@ -147,34 +170,36 @@ pcf8563_init(void) if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) goto err; - /* Clear the VL bit in the seconds register. */ - ret = rtc_read(RTC_SECONDS); - - if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0) + if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) goto err; - + /* Reset the alarms. */ - if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0) + if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0) + + if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_DAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) goto err; - - if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0) + + if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME); - - return 0; + + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); + } + + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit @@ -187,8 +212,8 @@ pcf8563_exit(void) * ioctl calls for this driver. Why return -ENOTTY upon error? Because * POSIX says so! */ -int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +int pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -198,124 +223,146 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned return -ENOTTY; switch (cmd) { - case RTC_RD_TIME: - { - struct rtc_time tm; - - spin_lock(&rtc_lock); - get_rtc_time(&tm); - - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) { - spin_unlock(&rtc_lock); - return -EFAULT; - } - - spin_unlock(&rtc_lock); - return 0; - } - break; - case RTC_SET_TIME: - { -#ifdef CONFIG_ETRAX_RTC_READONLY - return -EPERM; -#else - int leap; - int century; - struct rtc_time tm; - - memset(&tm, 0, sizeof (struct rtc_time)); - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time))) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 0; - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - BIN_TO_BCD(tm.tm_year); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_sec); - tm.tm_mon |= century; - - spin_lock(&rtc_lock); - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - - spin_unlock(&rtc_lock); - - return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ - } - - case RTC_VLOW_RD: - { - int vl_bit = 0; - - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; - - return 0; - } + case RTC_RD_TIME: + { + struct rtc_time tm; - case RTC_VLOW_SET: - { - /* Clear the VL bit in the seconds register */ - int ret = rtc_read(RTC_SECONDS); + mutex_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); + get_rtc_time(&tm); - rtc_write(RTC_SECONDS, (ret & 0x7F)); + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { + spin_unlock(&rtc_lock); + return -EFAULT; + } + + mutex_unlock(&rtc_lock); - return 0; + return 0; + } + case RTC_SET_TIME: + { + int leap; + int year; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof tm); + if (!capable(CAP_SYS_TIME)) + return -EPERM; + + if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + mutex_lock(&rtc_lock); + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_VL_READ: + if (voltage_low) { + printk(KERN_ERR "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); } - default: - return -ENOTTY; + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) + return -EFAULT; + return 0; + + case RTC_VL_CLR: + { + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ + + int ret = rtc_read(RTC_SECONDS); + + rtc_write(RTC_SECONDS, (ret & 0x7F)); + + /* Clear the cached value. */ + voltage_low = 0; + + return 0; + } + default: + return -ENOTTY; } return 0; } -static int __init -pcf8563_register(void) +static int __init pcf8563_register(void) { - pcf8563_init(); + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", PCF8563_NAME, PCF8563_MAJOR); return -1; } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - return 0; + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } + + return 0; } module_init(pcf8563_register); diff --git a/arch/cris/arch-v10/drivers/sync_serial.c b/arch/cris/arch-v10/drivers/sync_serial.c new file mode 100644 index 00000000000..069546e342c --- /dev/null +++ b/arch/cris/arch-v10/drivers/sync_serial.c @@ -0,0 +1,1441 @@ +/* + * Simple synchronous serial port driver for ETRAX 100LX. + * + * Synchronous serial ports are used for continuous streamed data like audio. + * The default setting for this driver is compatible with the STA 013 MP3 + * decoder. The driver can easily be tuned to fit other audio encoder/decoders + * and SPI + * + * Copyright (c) 2001-2008 Axis Communications AB + * + * Author: Mikael Starvik, Johan Adolfsson + * + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/io.h> +#include <asm/arch/svinto.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/sync_serial.h> +#include <asm/arch/io_interface_mux.h> + +/* The receiver is a bit tricky beacuse of the continuous stream of data.*/ +/* */ +/* Three DMA descriptors are linked together. Each DMA descriptor is */ +/* responsible for port->bufchunk of a common buffer. */ +/* */ +/* +---------------------------------------------+ */ +/* | +----------+ +----------+ +----------+ | */ +/* +-> | Descr[0] |-->| Descr[1] |-->| Descr[2] |-+ */ +/* +----------+ +----------+ +----------+ */ +/* | | | */ +/* v v v */ +/* +-------------------------------------+ */ +/* | BUFFER | */ +/* +-------------------------------------+ */ +/* |<- data_avail ->| */ +/* readp writep */ +/* */ +/* If the application keeps up the pace readp will be right after writep.*/ +/* If the application can't keep the pace we have to throw away data. */ +/* The idea is that readp should be ready with the data pointed out by */ +/* Descr[i] when the DMA has filled in Descr[i+1]. */ +/* Otherwise we will discard */ +/* the rest of the data pointed out by Descr1 and set readp to the start */ +/* of Descr2 */ + +#define SYNC_SERIAL_MAJOR 125 + +/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit */ +/* words can be handled */ +#define IN_BUFFER_SIZE 12288 +#define IN_DESCR_SIZE 256 +#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) +#define OUT_BUFFER_SIZE 4096 + +#define DEFAULT_FRAME_RATE 0 +#define DEFAULT_WORD_RATE 7 + +/* NOTE: Enabling some debug will likely cause overrun or underrun, + * especially if manual mode is use. + */ +#define DEBUG(x) +#define DEBUGREAD(x) +#define DEBUGWRITE(x) +#define DEBUGPOLL(x) +#define DEBUGRXINT(x) +#define DEBUGTXINT(x) + +/* Define some macros to access ETRAX 100 registers */ +#define SETF(var, reg, field, val) \ + do { \ + var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_FIELD_(reg##_, field##_, val); \ + } while (0) + +#define SETS(var, reg, field, val) \ + do { \ + var = (var & ~IO_MASK_(reg##_, field##_)) | \ + IO_STATE_(reg##_, field##_, _##val); \ + } while (0) + +struct sync_port { + /* Etrax registers and bits*/ + const volatile unsigned *const status; + volatile unsigned *const ctrl_data; + volatile unsigned *const output_dma_first; + volatile unsigned char *const output_dma_cmd; + volatile unsigned char *const output_dma_clr_irq; + volatile unsigned *const input_dma_first; + volatile unsigned char *const input_dma_cmd; + volatile unsigned *const input_dma_descr; + /* 8*4 */ + volatile unsigned char *const input_dma_clr_irq; + volatile unsigned *const data_out; + const volatile unsigned *const data_in; + char data_avail_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char transmitter_ready_bit; /* In R_IRQ_MASK1_RD/SET/CLR */ + char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */ + + char output_dma_bit; /* In R_IRQ_MASK2_RD */ + /* End of fields initialised in array */ + char started; /* 1 if port has been started */ + char port_nbr; /* Port 0 or 1 */ + char busy; /* 1 if port is busy */ + + char enabled; /* 1 if port is enabled */ + char use_dma; /* 1 if port uses dma */ + char tr_running; + + char init_irqs; + + /* Register shadow */ + unsigned int ctrl_data_shadow; + /* Remaining bytes for current transfer */ + volatile unsigned int out_count; + /* Current position in out_buffer */ + unsigned char *outp; + /* 16*4 */ + /* Next byte to be read by application */ + volatile unsigned char *volatile readp; + /* Next byte to be written by etrax */ + volatile unsigned char *volatile writep; + + unsigned int in_buffer_size; + unsigned int inbufchunk; + struct etrax_dma_descr out_descr __attribute__ ((aligned(32))); + struct etrax_dma_descr in_descr[NUM_IN_DESCR] __attribute__ ((aligned(32))); + unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); + unsigned char in_buffer[IN_BUFFER_SIZE]__attribute__ ((aligned(32))); + unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); + struct etrax_dma_descr *next_rx_desc; + struct etrax_dma_descr *prev_rx_desc; + int full; + + wait_queue_head_t out_wait_q; + wait_queue_head_t in_wait_q; +}; + + +static int etrax_sync_serial_init(void); +static void initialize_port(int portnbr); +static inline int sync_data_avail(struct sync_port *port); + +static int sync_serial_open(struct inode *inode, struct file *file); +static int sync_serial_release(struct inode *inode, struct file *file); +static unsigned int sync_serial_poll(struct file *filp, poll_table *wait); + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos); +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +#if ((defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))) +#define SYNC_SER_DMA +#endif + +static void send_word(struct sync_port *port); +static void start_dma(struct sync_port *port, const char *data, int count); +static void start_dma_in(struct sync_port *port); +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); +#endif +#if ((defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \ + (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \ + !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))) +#define SYNC_SER_MANUAL +#endif +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id); +#endif + +/* The ports */ +static struct sync_port ports[] = { + { + .status = R_SYNC_SERIAL1_STATUS, + .ctrl_data = R_SYNC_SERIAL1_CTRL, + .output_dma_first = R_DMA_CH8_FIRST, + .output_dma_cmd = R_DMA_CH8_CMD, + .output_dma_clr_irq = R_DMA_CH8_CLR_INTR, + .input_dma_first = R_DMA_CH9_FIRST, + .input_dma_cmd = R_DMA_CH9_CMD, + .input_dma_descr = R_DMA_CH9_DESCR, + .input_dma_clr_irq = R_DMA_CH9_CLR_INTR, + .data_out = R_SYNC_SERIAL1_TR_DATA, + .data_in = R_SYNC_SERIAL1_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma9_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma8_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + }, + { + .status = R_SYNC_SERIAL3_STATUS, + .ctrl_data = R_SYNC_SERIAL3_CTRL, + .output_dma_first = R_DMA_CH4_FIRST, + .output_dma_cmd = R_DMA_CH4_CMD, + .output_dma_clr_irq = R_DMA_CH4_CLR_INTR, + .input_dma_first = R_DMA_CH5_FIRST, + .input_dma_cmd = R_DMA_CH5_CMD, + .input_dma_descr = R_DMA_CH5_DESCR, + .input_dma_clr_irq = R_DMA_CH5_CLR_INTR, + .data_out = R_SYNC_SERIAL3_TR_DATA, + .data_in = R_SYNC_SERIAL3_REC_DATA, + .data_avail_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_data), + .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_ready), + .input_dma_descr_bit = IO_BITNR(R_IRQ_MASK2_RD, dma5_descr), + .output_dma_bit = IO_BITNR(R_IRQ_MASK2_RD, dma4_eop), + .init_irqs = 1, +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + .use_dma = 1, +#else + .use_dma = 0, +#endif + } +}; + +/* Register shadows */ +static unsigned sync_serial_prescale_shadow; + +#define NUMBER_OF_PORTS 2 + +static struct file_operations sync_serial_fops = { + .owner = THIS_MODULE, + .write = sync_serial_write, + .read = sync_serial_read, + .poll = sync_serial_poll, + .ioctl = sync_serial_ioctl, + .open = sync_serial_open, + .release = sync_serial_release +}; + +static int __init etrax_sync_serial_init(void) +{ + ports[0].enabled = 0; + ports[1].enabled = 0; + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + if (cris_request_io_interface(if_sync_serial_1, "sync_ser1")) { + printk(KERN_CRIT "ETRAX100LX sync_serial: " + "Could not allocate IO group for port %d\n", 0); + return -EBUSY; + } +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + if (cris_request_io_interface(if_sync_serial_3, "sync_ser3")) { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk(KERN_CRIT "ETRAX100LX sync_serial: " + "Could not allocate IO group for port %d\n", 1); + return -EBUSY; + } +#endif + + if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial", + &sync_serial_fops) < 0) { +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + cris_free_io_interface(if_sync_serial_3); +#endif +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + cris_free_io_interface(if_sync_serial_1); +#endif + printk("unable to get major for synchronous serial port\n"); + return -EBUSY; + } + + /* Deselect synchronous serial ports while configuring. */ + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + /* Initialize Ports */ +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) + ports[0].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser1, ss1extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) + ports[0].use_dma = 1; +#else + ports[0].use_dma = 0; +#endif + initialize_port(0); +#endif + +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) + ports[1].enabled = 1; + SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser3, ss3extra); + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); +#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA) + ports[1].use_dma = 1; +#else + ports[1].use_dma = 0; +#endif + initialize_port(1); +#endif + + *R_PORT_PB_I2C = port_pb_i2c_shadow; /* Use PB4/PB7 */ + + /* Set up timing */ + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow = ( + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u1, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u3, external) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, prescaler, div4) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, frame_rate, + DEFAULT_FRAME_RATE) | + IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, DEFAULT_WORD_RATE) | + IO_STATE(R_SYNC_SERIAL_PRESCALE, warp_mode, normal)); + + /* Select synchronous ports */ + *R_GEN_CONFIG_II = gen_config_ii_shadow; + + printk(KERN_INFO "ETRAX 100LX synchronous serial port driver\n"); + return 0; +} + +static void __init initialize_port(int portnbr) +{ + struct sync_port *port = &ports[portnbr]; + + DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr)); + + port->started = 0; + port->port_nbr = portnbr; + port->busy = 0; + port->tr_running = 0; + + port->out_count = 0; + port->outp = port->out_buffer; + + port->readp = port->flip; + port->writep = port->flip; + port->in_buffer_size = IN_BUFFER_SIZE; + port->inbufchunk = IN_DESCR_SIZE; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc->ctrl = d_eol; + + init_waitqueue_head(&port->out_wait_q); + init_waitqueue_head(&port->in_wait_q); + + port->ctrl_data_shadow = + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_baud, c115k2Hz) | + IO_STATE(R_SYNC_SERIAL1_CTRL, mode, master_output) | + IO_STATE(R_SYNC_SERIAL1_CTRL, error, ignore) | + IO_STATE(R_SYNC_SERIAL1_CTRL, rec_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_synctype, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_syncsize, word) | + IO_STATE(R_SYNC_SERIAL1_CTRL, f_sync, on) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_mode, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_halt, stopped) | + IO_STATE(R_SYNC_SERIAL1_CTRL, bitorder, msb) | + IO_STATE(R_SYNC_SERIAL1_CTRL, tr_enable, disable) | + IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_empty, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, buf_full, lmt_8) | + IO_STATE(R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled) | + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_polarity, neg) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_polarity, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, status_polarity, inverted)| + IO_STATE(R_SYNC_SERIAL1_CTRL, clk_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, frame_driver, normal) | + IO_STATE(R_SYNC_SERIAL1_CTRL, status_driver, normal)| + IO_STATE(R_SYNC_SERIAL1_CTRL, def_out0, high); + + if (port->use_dma) + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, + dma_enable, on); + else + port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, + dma_enable, off); + + *port->ctrl_data = port->ctrl_data_shadow; +} + +static inline int sync_data_avail(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- - ----- + * ^rp ^wp ^wp ^rp + */ + if (end >= start) + avail = end - start; + else + avail = port->in_buffer_size - (start - end); + return avail; +} + +static inline int sync_data_avail_to_end(struct sync_port *port) +{ + int avail; + unsigned char *start; + unsigned char *end; + + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + /* 0123456789 0123456789 + * ----- ----- + * ^rp ^wp ^wp ^rp + */ + + if (end >= start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + return avail; +} + + +static int sync_serial_open(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + struct sync_port *port; + int mode; + + DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev)); + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + /* Allow open this device twice (assuming one reader and one writer) */ + if (port->busy == 2) { + DEBUG(printk(KERN_DEBUG "Device is busy.. \n")); + return -EBUSY; + } + if (port->init_irqs) { + if (port->use_dma) { + if (port == &ports[0]) { +#ifdef SYNC_SER_DMA + if (request_irq(24, tr_interrupt, 0, + "synchronous serial 1 dma tr", + &ports[0])) { + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 IRQ"); + return -EBUSY; + } else if (request_irq(25, rx_interrupt, 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(24, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 IRQ"); + return -EBUSY; + } else if (cris_request_dma(8, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 " + "TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(9, + "synchronous serial 1 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser1)) { + cris_free_dma(8, NULL); + free_irq(24, &port[0]); + free_irq(25, &port[0]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 1 " + "RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(8); WAIT_DMA(8); + RESET_DMA(9); WAIT_DMA(9); + *R_DMA_CH8_CLR_INTR = + IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, + do); + *R_DMA_CH9_CLR_INTR = + IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH9_CLR_INTR, clr_descr, + do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma8_eop, + set) | + IO_STATE(R_IRQ_MASK2_SET, dma9_descr, + set); + } else if (port == &ports[1]) { +#ifdef SYNC_SER_DMA + if (request_irq(20, tr_interrupt, 0, + "synchronous serial 3 dma tr", + &ports[1])) { + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 IRQ"); + return -EBUSY; + } else if (request_irq(21, rx_interrupt, 0, + "synchronous serial 3 dma rx", + &ports[1])) { + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 IRQ"); + return -EBUSY; + } else if (cris_request_dma(4, + "synchronous serial 3 dma tr", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 " + "TX DMA channel"); + return -EBUSY; + } else if (cris_request_dma(5, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + dma_ser3)) { + cris_free_dma(4, NULL); + free_irq(21, &ports[1]); + free_irq(20, &ports[1]); + printk(KERN_CRIT "Can't alloc " + "sync serial port 3 " + "RX DMA channel"); + return -EBUSY; + } +#endif + RESET_DMA(4); WAIT_DMA(4); + RESET_DMA(5); WAIT_DMA(5); + *R_DMA_CH4_CLR_INTR = + IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, + do); + *R_DMA_CH5_CLR_INTR = + IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop, + do) | + IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr, + do); + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma4_eop, + set) | + IO_STATE(R_IRQ_MASK2_SET, dma5_descr, + set); + } + start_dma_in(port); + port->init_irqs = 0; + } else { /* !port->use_dma */ +#ifdef SYNC_SER_MANUAL + if (port == &ports[0]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[0])) { + printk(KERN_CRIT "Can't alloc " + "sync serial manual irq"); + return -EBUSY; + } + } else if (port == &ports[1]) { + if (request_irq(8, + manual_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "synchronous serial manual irq", + &ports[1])) { + printk(KERN_CRIT "Can't alloc " + "sync serial manual irq"); + return -EBUSY; + } + } + port->init_irqs = 0; +#else + panic("sync_serial: Manual mode not supported.\n"); +#endif /* SYNC_SER_MANUAL */ + } + } /* port->init_irqs */ + + port->busy++; + /* Start port if we use it as input */ + mode = IO_EXTRACT(R_SYNC_SERIAL1_CTRL, mode, port->ctrl_data_shadow); + if (mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_input) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_bidir) || + mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_bidir)) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + *port->ctrl_data = port->ctrl_data_shadow; + if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + DEBUG(printk(KERN_DEBUG "sser%d rec started\n", dev)); + } + return 0; +} + +static int sync_serial_release(struct inode *inode, struct file *file) +{ + int dev = MINOR(inode->i_rdev); + struct sync_port *port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + if (port->busy) + port->busy--; + if (!port->busy) + *R_IRQ_MASK1_CLR = ((1 << port->data_avail_bit) | + (1 << port->transmitter_ready_bit)); + + return 0; +} + + + +static unsigned int sync_serial_poll(struct file *file, poll_table *wait) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + unsigned int mask = 0; + struct sync_port *port; + DEBUGPOLL(static unsigned int prev_mask = 0); + + port = &ports[dev]; + poll_wait(file, &port->out_wait_q, wait); + poll_wait(file, &port->in_wait_q, wait); + /* Some room to write */ + if (port->out_count < OUT_BUFFER_SIZE) + mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ + if (sync_data_avail(port) >= port->inbufchunk) + mask |= POLLIN | POLLRDNORM; + + DEBUGPOLL(if (mask != prev_mask) + printk(KERN_DEBUG "sync_serial_poll: mask 0x%08X %s %s\n", + mask, + mask & POLLOUT ? "POLLOUT" : "", + mask & POLLIN ? "POLLIN" : ""); + prev_mask = mask; + ); + return mask; +} + +static int sync_serial_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int return_val = 0; + unsigned long flags; + + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + struct sync_port *port; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -1; + } + port = &ports[dev]; + + local_irq_save(flags); + /* Disable port while changing config */ + if (dev) { + if (port->use_dma) { + RESET_DMA(4); WAIT_DMA(4); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH4_CLR_INTR = + IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async); + } else { + if (port->use_dma) { + RESET_DMA(8); WAIT_DMA(8); + port->tr_running = 0; + port->out_count = 0; + port->outp = port->out_buffer; + *R_DMA_CH8_CLR_INTR = + IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); + } + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async); + } + *R_GEN_CONFIG_II = gen_config_ii_shadow; + local_irq_restore(flags); + + switch (cmd) { + case SSP_SPEED: + if (GET_SPEED(arg) == CODEC) { + if (dev) + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u3, + codec); + else + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u1, + codec); + + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, prescaler, + GET_FREQ(arg)); + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, frame_rate, + GET_FRAME_RATE(arg)); + SETF(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, word_rate, + GET_WORD_RATE(arg)); + } else { + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + tr_baud, GET_SPEED(arg)); + if (dev) + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u3, + baudrate); + else + SETS(sync_serial_prescale_shadow, + R_SYNC_SERIAL_PRESCALE, clk_sel_u1, + baudrate); + } + break; + case SSP_MODE: + if (arg > 5) + return -EINVAL; + if (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT) + *R_IRQ_MASK1_CLR = 1 << port->data_avail_bit; + else if (!port->use_dma) + *R_IRQ_MASK1_SET = 1 << port->data_avail_bit; + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, arg); + break; + case SSP_FRAME_SYNC: + if (arg & NORMAL_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_synctype, normal); + else if (arg & EARLY_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_synctype, early); + + if (arg & BIT_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, bit); + else if (arg & WORD_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, word); + else if (arg & EXTENDED_SYNC) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_syncsize, extended); + + if (arg & SYNC_ON) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_sync, on); + else if (arg & SYNC_OFF) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + f_sync, off); + + if (arg & WORD_SIZE_8) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size8bit); + else if (arg & WORD_SIZE_12) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size12bit); + else if (arg & WORD_SIZE_16) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size16bit); + else if (arg & WORD_SIZE_24) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size24bit); + else if (arg & WORD_SIZE_32) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + wordsize, size32bit); + + if (arg & BIT_ORDER_MSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + bitorder, msb); + else if (arg & BIT_ORDER_LSB) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + bitorder, lsb); + + if (arg & FLOW_CONTROL_ENABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + flow_ctrl, enabled); + else if (arg & FLOW_CONTROL_DISABLE) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + flow_ctrl, disabled); + + if (arg & CLOCK_NOT_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_mode, normal); + else if (arg & CLOCK_GATED) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_mode, gated); + + break; + case SSP_IPOLARITY: + /* NOTE!! negedge is considered NORMAL */ + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, neg); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, pos); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_polarity, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_polarity, inverted); + break; + case SSP_OPOLARITY: + if (arg & CLOCK_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, normal); + else if (arg & CLOCK_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, inverted); + + if (arg & FRAME_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, normal); + else if (arg & FRAME_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, inverted); + + if (arg & STATUS_NORMAL) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_driver, normal); + else if (arg & STATUS_INVERT) + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + status_driver, inverted); + break; + case SSP_SPI: + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, + disabled); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, + msb); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, + size8bit); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, + word); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, + normal); + if (arg & SPI_SLAVE) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_polarity, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_polarity, neg); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + mode, SLAVE_INPUT); + } else { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + frame_driver, inverted); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + clk_driver, inverted); + SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, + mode, MASTER_OUTPUT); + } + break; + case SSP_INBUFCHUNK: +#if 0 + if (arg > port->in_buffer_size/NUM_IN_DESCR) + return -EINVAL; + port->inbufchunk = arg; + /* Make sure in_buffer_size is a multiple of inbufchunk */ + port->in_buffer_size = + (port->in_buffer_size/port->inbufchunk) * + port->inbufchunk; + DEBUG(printk(KERN_DEBUG "inbufchunk %i in_buffer_size: %i\n", + port->inbufchunk, port->in_buffer_size)); + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } +#endif + break; + default: + return_val = -1; + } + /* Make sure we write the config without interruption */ + local_irq_save(flags); + /* Set config and enable port */ + *port->ctrl_data = port->ctrl_data_shadow; + nop(); nop(); nop(); nop(); + *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow; + nop(); nop(); nop(); nop(); + if (dev) + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync); + else + SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync); + + *R_GEN_CONFIG_II = gen_config_ii_shadow; + /* Reset DMA. At readout from serial port the data could be shifted + * one byte if not resetting DMA. + */ + if (port->use_dma) { + if (port->port_nbr == 0) { + RESET_DMA(9); + WAIT_DMA(9); + } else { + RESET_DMA(5); + WAIT_DMA(5); + } + start_dma_in(port); + } + local_irq_restore(flags); + return return_val; +} + + +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct sync_port *port; + unsigned long flags; + unsigned long c, c1; + unsigned long free_outp; + unsigned long outp; + unsigned long out_buffer; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu (%d/%d)\n", + port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); + /* Space to end of buffer */ + /* + * out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE + * outp^ +out_count + * ^free_outp + * out_buffer 45<- c ->0123OUT_BUFFER_SIZE + * +out_count outp^ + * free_outp + * + */ + + /* Read variables that may be updated by interrupts */ + local_irq_save(flags); + if (count > OUT_BUFFER_SIZE - port->out_count) + count = OUT_BUFFER_SIZE - port->out_count; + + outp = (unsigned long)port->outp; + free_outp = outp + port->out_count; + local_irq_restore(flags); + out_buffer = (unsigned long)port->out_buffer; + + /* Find out where and how much to write */ + if (free_outp >= out_buffer + OUT_BUFFER_SIZE) + free_outp -= OUT_BUFFER_SIZE; + if (free_outp >= outp) + c = out_buffer + OUT_BUFFER_SIZE - free_outp; + else + c = outp - free_outp; + if (c > count) + c = count; + + DEBUGWRITE(printk(KERN_DEBUG "w op %08lX fop %08lX c %lu\n", + outp, free_outp, c)); + if (copy_from_user((void *)free_outp, buf, c)) + return -EFAULT; + + if (c != count) { + buf += c; + c1 = count - c; + DEBUGWRITE(printk(KERN_DEBUG "w2 fi %lu c %lu c1 %lu\n", + free_outp-out_buffer, c, c1)); + if (copy_from_user((void *)out_buffer, buf, c1)) + return -EFAULT; + } + local_irq_save(flags); + port->out_count += count; + local_irq_restore(flags); + + /* Make sure transmitter/receiver is running */ + if (!port->started) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + } + + *port->ctrl_data = port->ctrl_data_shadow; + + if (file->f_flags & O_NONBLOCK) { + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << + port->transmitter_ready_bit; + } else + start_dma(port, + (unsigned char *volatile)port->outp, c); + } + local_irq_restore(flags); + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu NB\n", + port->port_nbr, count)); + return count; + } + + /* Sleep until all sent */ + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + local_irq_save(flags); + if (!port->tr_running) { + if (!port->use_dma) { + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + *R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit; + } else + start_dma(port, port->outp, c); + } + local_irq_restore(flags); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) + return -EINTR; + + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n", port->port_nbr, count)); + return count; +} + +static ssize_t sync_serial_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + int dev = MINOR(file->f_dentry->d_inode->i_rdev); + int avail; + struct sync_port *port; + unsigned char *start; + unsigned char *end; + unsigned long flags; + + if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) { + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); + return -ENODEV; + } + port = &ports[dev]; + + DEBUGREAD(printk(KERN_DEBUG "R%d c %d ri %lu wi %lu /%lu\n", + dev, count, port->readp - port->flip, + port->writep - port->flip, port->in_buffer_size)); + + if (!port->started) { + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, + running); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, + enable); + SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, + enable); + port->started = 1; + } + *port->ctrl_data = port->ctrl_data_shadow; + + /* Calculate number of available bytes */ + /* Save pointers to avoid that they are modified by interrupt */ + local_irq_save(flags); + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + local_irq_restore(flags); + while (start == end && !port->full) { + /* No data */ + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + interruptible_sleep_on(&port->in_wait_q); + if (signal_pending(current)) + return -EINTR; + + local_irq_save(flags); + start = (unsigned char *)port->readp; /* cast away volatile */ + end = (unsigned char *)port->writep; /* cast away volatile */ + local_irq_restore(flags); + } + + /* Lazy read, never return wrapped data. */ + if (port->full) + avail = port->in_buffer_size; + else if (end > start) + avail = end - start; + else + avail = port->flip + port->in_buffer_size - start; + + count = count > avail ? avail : count; + if (copy_to_user(buf, start, count)) + return -EFAULT; + /* Disable interrupts while updating readp */ + local_irq_save(flags); + port->readp += count; + if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */ + port->readp = port->flip; + port->full = 0; + local_irq_restore(flags); + DEBUGREAD(printk(KERN_DEBUG "r %d\n", count)); + return count; +} + +static void send_word(struct sync_port *port) +{ + switch (IO_EXTRACT(R_SYNC_SERIAL1_CTRL, wordsize, + port->ctrl_data_shadow)) { + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + port->out_count--; + *port->data_out = *port->outp++; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = (*port->outp++) << 8; + data |= *port->outp++; + port->out_count -= 2; + *port->data_out = data; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + port->out_count -= 2; + *port->data_out = *(unsigned short *)port->outp; + port->outp += 2; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + port->out_count -= 3; + *port->data_out = *(unsigned int *)port->outp; + port->outp += 3; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + port->out_count -= 4; + *port->data_out = *(unsigned int *)port->outp; + port->outp += 4; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + break; + } +} + + +static void start_dma(struct sync_port *port, const char *data, int count) +{ + port->tr_running = 1; + port->out_descr.hw_len = 0; + port->out_descr.next = 0; + port->out_descr.ctrl = d_eol | d_eop; /* No d_wait to avoid glitches */ + port->out_descr.sw_len = count; + port->out_descr.buf = virt_to_phys(data); + port->out_descr.status = 0; + + *port->output_dma_first = virt_to_phys(&port->out_descr); + *port->output_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); + DEBUGTXINT(printk(KERN_DEBUG "dma %08lX c %d\n", + (unsigned long)data, count)); +} + +static void start_dma_in(struct sync_port *port) +{ + int i; + unsigned long buf; + port->writep = port->flip; + + if (port->writep > port->flip + port->in_buffer_size) { + panic("Offset too large in sync serial driver\n"); + return; + } + buf = virt_to_phys(port->in_buffer); + for (i = 0; i < NUM_IN_DESCR; i++) { + port->in_descr[i].sw_len = port->inbufchunk; + port->in_descr[i].ctrl = d_int; + port->in_descr[i].next = virt_to_phys(&port->in_descr[i+1]); + port->in_descr[i].buf = buf; + port->in_descr[i].hw_len = 0; + port->in_descr[i].status = 0; + port->in_descr[i].fifo_len = 0; + buf += port->inbufchunk; + prepare_rx_descriptor(&port->in_descr[i]); + } + /* Link the last descriptor to the first */ + port->in_descr[i-1].next = virt_to_phys(&port->in_descr[0]); + port->in_descr[i-1].ctrl |= d_eol; + port->next_rx_desc = &port->in_descr[0]; + port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + *port->input_dma_first = virt_to_phys(port->next_rx_desc); + *port->input_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start); +} + +#ifdef SYNC_SER_DMA +static irqreturn_t tr_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + struct etrax_dma_descr *descr; + unsigned int sentl; + int handled = 0; + int i; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + if (!port->enabled || !port->use_dma) + continue; + + /* IRQ active for the port? */ + if (!(ireg & (1 << port->output_dma_bit))) + continue; + + handled = 1; + + /* Clear IRQ */ + *port->output_dma_clr_irq = + IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) | + IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do); + + descr = &port->out_descr; + if (!(descr->status & d_stop)) + sentl = descr->sw_len; + else + /* Otherwise find amount of data sent here */ + sentl = descr->hw_len; + + port->out_count -= sentl; + port->outp += sentl; + if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) + port->outp = port->out_buffer; + if (port->out_count) { + int c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; + if (c > port->out_count) + c = port->out_count; + DEBUGTXINT(printk(KERN_DEBUG + "tx_int DMAWRITE %i %i\n", sentl, c)); + start_dma(port, port->outp, c); + } else { + DEBUGTXINT(printk(KERN_DEBUG + "tx_int DMA stop %i\n", sentl)); + port->tr_running = 0; + } + /* wake up the waiting process */ + wake_up_interruptible(&port->out_wait_q); + } + return IRQ_RETVAL(handled); +} /* tr_interrupt */ + +static irqreturn_t rx_interrupt(int irq, void *dev_id) +{ + unsigned long ireg = *R_IRQ_MASK2_RD; + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + + if (!port->enabled || !port->use_dma) + continue; + + if (!(ireg & (1 << port->input_dma_descr_bit))) + continue; + + /* Descriptor interrupt */ + handled = 1; + while (*port->input_dma_descr != + virt_to_phys(port->next_rx_desc)) { + if (port->writep + port->inbufchunk > port->flip + + port->in_buffer_size) { + int first_size = port->flip + + port->in_buffer_size - port->writep; + memcpy(port->writep, + phys_to_virt(port->next_rx_desc->buf), + first_size); + memcpy(port->flip, + phys_to_virt(port->next_rx_desc->buf + + first_size), + port->inbufchunk - first_size); + port->writep = port->flip + + port->inbufchunk - first_size; + } else { + memcpy(port->writep, + phys_to_virt(port->next_rx_desc->buf), + port->inbufchunk); + port->writep += port->inbufchunk; + if (port->writep >= port->flip + + port->in_buffer_size) + port->writep = port->flip; + } + if (port->writep == port->readp) + port->full = 1; + prepare_rx_descriptor(port->next_rx_desc); + port->next_rx_desc->ctrl |= d_eol; + port->prev_rx_desc->ctrl &= ~d_eol; + port->prev_rx_desc = phys_to_virt((unsigned) + port->next_rx_desc); + port->next_rx_desc = phys_to_virt((unsigned) + port->next_rx_desc->next); + /* Wake up the waiting process */ + wake_up_interruptible(&port->in_wait_q); + *port->input_dma_cmd = IO_STATE(R_DMA_CH1_CMD, + cmd, restart); + /* DMA has reached end of descriptor */ + *port->input_dma_clr_irq = IO_STATE(R_DMA_CH0_CLR_INTR, + clr_descr, do); + } + } + return IRQ_RETVAL(handled); +} /* rx_interrupt */ +#endif /* SYNC_SER_DMA */ + +#ifdef SYNC_SER_MANUAL +static irqreturn_t manual_interrupt(int irq, void *dev_id) +{ + int i; + int handled = 0; + + for (i = 0; i < NUMBER_OF_PORTS; i++) { + struct sync_port *port = &ports[i]; + + if (!port->enabled || port->use_dma) + continue; + + /* Data received? */ + if (*R_IRQ_MASK1_RD & (1 << port->data_avail_bit)) { + handled = 1; + /* Read data */ + switch (port->ctrl_data_shadow & + IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize)) { + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit): + *port->writep++ = + *(volatile char *)port->data_in; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit): + { + int data = *(unsigned short *)port->data_in; + *port->writep = (data & 0x0ff0) >> 4; + *(port->writep + 1) = data & 0x0f; + port->writep += 2; + break; + } + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit): + *(unsigned short *)port->writep = + *(volatile unsigned short *)port->data_in; + port->writep += 2; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit): + *(unsigned int *)port->writep = *port->data_in; + port->writep += 3; + break; + case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit): + *(unsigned int *)port->writep = *port->data_in; + port->writep += 4; + break; + } + + /* Wrap? */ + if (port->writep >= port->flip + port->in_buffer_size) + port->writep = port->flip; + if (port->writep == port->readp) { + /* Receive buffer overrun, discard oldest */ + port->readp++; + /* Wrap? */ + if (port->readp >= port->flip + + port->in_buffer_size) + port->readp = port->flip; + } + if (sync_data_avail(port) >= port->inbufchunk) { + /* Wake up application */ + wake_up_interruptible(&port->in_wait_q); + } + } + + /* Transmitter ready? */ + if (*R_IRQ_MASK1_RD & (1 << port->transmitter_ready_bit)) { + if (port->out_count > 0) { + /* More data to send */ + send_word(port); + } else { + /* Transmission finished */ + /* Turn off IRQ */ + *R_IRQ_MASK1_CLR = 1 << + port->transmitter_ready_bit; + /* Wake up application */ + wake_up_interruptible(&port->out_wait_q); + } + } + } + return IRQ_RETVAL(handled); +} +#endif + +module_init(etrax_sync_serial_init); diff --git a/arch/cris/arch-v10/kernel/debugport.c b/arch/cris/arch-v10/kernel/debugport.c index 93679a48c79..04d5eee2c90 100644 --- a/arch/cris/arch-v10/kernel/debugport.c +++ b/arch/cris/arch-v10/kernel/debugport.c @@ -1,6 +1,6 @@ /* Serialport functions for debugging * - * Copyright (c) 2000 Axis Communications AB + * Copyright (c) 2000-2007 Axis Communications AB * * Authors: Bjorn Wesen * @@ -11,96 +11,6 @@ * enableDebugIRQ() * init_etrax_debug() * - * $Log: debugport.c,v $ - * Revision 1.27 2005/06/10 10:34:14 starvik - * Real console support - * - * Revision 1.26 2005/06/07 07:06:07 starvik - * Added LF->CR translation to make ETRAX customers happy. - * - * Revision 1.25 2005/03/08 08:56:47 mikaelam - * Do only set index as port->index if port is defined, otherwise use the index from the command line - * - * Revision 1.24 2005/01/19 10:26:33 mikaelam - * Return the cris serial driver in console device driver callback function - * - * Revision 1.23 2005/01/14 10:12:17 starvik - * KGDB on separate port. - * Console fixes from 2.4. - * - * Revision 1.22 2005/01/11 16:06:13 starvik - * typo - * - * Revision 1.21 2005/01/11 13:49:14 starvik - * Added raw_printk to be used where we don't trust the console. - * - * Revision 1.20 2004/12/27 11:18:32 starvik - * Merge of Linux 2.6.10 (not functional yet). - * - * Revision 1.19 2004/10/21 07:26:16 starvik - * Made it possible to specify console settings on kernel command line. - * - * Revision 1.18 2004/10/19 13:07:37 starvik - * Merge of Linux 2.6.9 - * - * Revision 1.17 2004/09/29 10:33:46 starvik - * Resolved a dealock when printing debug from kernel. - * - * Revision 1.16 2004/08/24 06:12:19 starvik - * Whitespace cleanup - * - * Revision 1.15 2004/08/16 12:37:19 starvik - * Merge of Linux 2.6.8 - * - * Revision 1.14 2004/05/17 13:11:29 starvik - * Disable DMA until real serial driver is up - * - * Revision 1.13 2004/05/14 07:58:01 starvik - * Merge of changes from 2.4 - * - * Revision 1.12 2003/09/11 07:29:49 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.11 2003/07/07 09:53:36 starvik - * Revert all the 2.5.74 merge changes to make the console work again - * - * Revision 1.9 2003/02/17 17:07:23 starvik - * Solved the problem with corrupted debug output (from Linux 2.4) - * * Wait until DMA, FIFO and pipe is empty before and after transmissions - * * Buffer data until a FIFO flush can be triggered. - * - * Revision 1.8 2003/01/22 06:48:36 starvik - * Fixed warnings issued by GCC 3.2.1 - * - * Revision 1.7 2002/12/12 08:26:32 starvik - * Don't use C-comments inside CVS comments - * - * Revision 1.6 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ - * - * Revision 1.5 2002/11/20 06:58:03 starvik - * Compiles with kgdb - * - * Revision 1.4 2002/11/19 14:35:24 starvik - * Changes from linux 2.4 - * Changed struct initializer syntax to the currently preferred notation - * - * Revision 1.3 2002/11/06 09:47:03 starvik - * Modified for new interrupt macros - * - * Revision 1.2 2002/01/21 15:21:50 bjornw - * Update for kdev_t changes - * - * Revision 1.6 2001/04/17 13:58:39 orjanf - * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. - * - * Revision 1.5 2001/03/26 14:22:05 bjornw - * Namechange of some config options - * - * Revision 1.4 2000/10/06 12:37:26 bjornw - * Use physical addresses when talking to DMA - * - * */ #include <linux/console.h> @@ -112,6 +22,8 @@ #include <asm/arch/svinto.h> #include <asm/io.h> /* Get SIMCOUT. */ +extern void reset_watchdog(void); + struct dbg_port { unsigned int index; @@ -188,7 +100,9 @@ struct dbg_port ports[]= } }; +#ifdef CONFIG_ETRAX_SERIAL extern struct tty_driver *serial_driver; +#endif struct dbg_port* port = #if defined(CONFIG_ETRAX_DEBUG_PORT0) @@ -368,11 +282,12 @@ console_write_direct(struct console *co, const char *buf, unsigned int len) { int i; unsigned long flags; - local_irq_save(flags); if (!port) return; + local_irq_save(flags); + /* Send data */ for (i = 0; i < len; i++) { /* LF -> CRLF */ @@ -386,26 +301,16 @@ console_write_direct(struct console *co, const char *buf, unsigned int len) ; *port->write = buf[i]; } - local_irq_restore(flags); -} -int raw_printk(const char *fmt, ...) -{ - static char buf[1024]; - int printed_len; - static int first = 1; - if (first) { - /* Force reinitialization of the port to get manual mode. */ - port->started = 0; - start_port(port); - first = 0; - } - va_list args; - va_start(args, fmt); - printed_len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - console_write_direct(NULL, buf, strlen(buf)); - return printed_len; + /* + * Feed the watchdog, otherwise it will reset the chip during boot. + * The time to send an ordinary boot message line (10-90 chars) + * varies between 1-8ms at 115200. What makes up for the additional + * 90ms that allows the watchdog to bite? + */ + reset_watchdog(); + + local_irq_restore(flags); } static void @@ -500,6 +405,7 @@ console_setup(struct console *co, char *options) return 0; } + /* This is a dummy serial device that throws away anything written to it. * This is used when no debug output is wanted. */ @@ -555,7 +461,13 @@ etrax_console_device(struct console* co, int *index) { if (port) *index = port->index; + else + *index = 0; +#ifdef CONFIG_ETRAX_SERIAL return port ? serial_driver : &dummy_driver; +#else + return &dummy_driver; +#endif } static struct console sercons = { diff --git a/arch/cris/arch-v10/kernel/dma.c b/arch/cris/arch-v10/kernel/dma.c index e9a0311b141..eb1fa0d2b49 100644 --- a/arch/cris/arch-v10/kernel/dma.c +++ b/arch/cris/arch-v10/kernel/dma.c @@ -1,6 +1,5 @@ /* Wrapper for DMA channel allocator that updates DMA client muxing. - * Copyright 2004, Axis Communications AB - * $Id: dma.c,v 1.1 2004/12/13 12:21:51 starvik Exp $ + * Copyright 2004-2007, Axis Communications AB */ #include <linux/kernel.h> diff --git a/arch/cris/arch-v10/kernel/entry.S b/arch/cris/arch-v10/kernel/entry.S index d1361dc119e..3a65f322ae0 100644 --- a/arch/cris/arch-v10/kernel/entry.S +++ b/arch/cris/arch-v10/kernel/entry.S @@ -1,252 +1,9 @@ -/* $Id: entry.S,v 1.28 2005/06/20 05:06:30 starvik Exp $ - * +/* * linux/arch/cris/entry.S * * Copyright (C) 2000, 2001, 2002 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) - * - * $Log: entry.S,v $ - * Revision 1.28 2005/06/20 05:06:30 starvik - * Remove unnecessary diff to kernel.org tree - * - * Revision 1.27 2005/03/04 08:16:16 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.26 2005/01/11 13:49:47 starvik - * Added NMI handler. - * - * Revision 1.25 2004/12/27 11:18:32 starvik - * Merge of Linux 2.6.10 (not functional yet). - * - * Revision 1.24 2004/12/22 10:41:23 starvik - * Updates to make v10 compile with the latest SMP aware generic code (even - * though v10 will never have SMP). - * - * Revision 1.23 2004/10/19 13:07:37 starvik - * Merge of Linux 2.6.9 - * - * Revision 1.22 2004/06/21 10:29:55 starvik - * Merge of Linux 2.6.7 - * - * Revision 1.21 2004/06/09 05:30:27 starvik - * Clean up multiple interrupt handling. - * Prevent interrupts from interrupting each other. - * Handle all active interrupts. - * - * Revision 1.20 2004/06/08 08:55:32 starvik - * Removed unused code - * - * Revision 1.19 2004/06/04 11:56:15 starvik - * Implemented page table lookup for refills in assembler for improved performance. - * - * Revision 1.18 2004/05/11 12:28:25 starvik - * Merge of Linux 2.6.6 - * - * Revision 1.17 2003/09/11 07:29:49 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.16 2003/07/04 08:27:41 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.15 2003/04/09 07:32:55 starvik - * resume should return task_struct, not thread_info - * - * Revision 1.14 2003/04/09 05:20:44 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.13 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c - * - * Revision 1.12 2002/12/10 09:00:10 starvik - * Merge of Linux 2.5.51 - * - * Revision 1.11 2002/12/05 07:53:10 starvik - * Corrected constants used with btstq - * - * Revision 1.10 2002/11/27 08:45:10 starvik - * pid is in task_struct, not thread_info - * - * Revision 1.9 2002/11/26 09:52:05 starvik - * Added preemptive kernel scheduling (if CONFIG_PREEMPT) - * - * Revision 1.8 2002/11/20 11:56:11 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.7 2002/11/18 13:02:42 starvik - * Added fourth parameter to do_notify_resume - * Minor cleanup - * - * Revision 1.6 2002/11/11 10:37:50 starvik - * Use new asm-offset defines - * Modified for new location of current->work etc - * Removed SYMBOL_NAME from syscalls - * Added some new syscalls - * - * Revision 1.5 2002/11/05 06:45:11 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.4 2002/02/05 15:41:31 bjornw - * Rewritten to conform better to current 2.5 code (similar to arch/i386) - * - * Revision 1.3 2002/01/21 15:22:20 bjornw - * NICE_DOGGY fix from 2.4 arch/cris - * - * Revision 1.37 2001/12/07 17:03:55 bjornw - * Call a c-hook called watchdog_bite_hook instead of show_registers directly - * - * Revision 1.36 2001/11/22 13:36:36 bjornw - * * In ret_from_intr, check regs->dccr for usermode reentrance instead of - * DCCR explicitly (because the latter might not reflect current reality) - * * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before - * since $r9 is call-clobbered and is potentially needed afterwards - * - * Revision 1.35 2001/10/30 17:10:15 bjornw - * Add some syscalls - * - * Revision 1.34 2001/10/01 14:45:03 bjornw - * Removed underscores and added register prefixes - * - * Revision 1.33 2001/08/21 13:48:01 jonashg - * Added fix by HP to avoid oops when doing a hard_reset_now. - * - * Revision 1.32 2001/08/14 04:32:02 hp - * In _resume, add comment why R9 is saved; don't sound like it's call-saved. - * - * Revision 1.31 2001/07/25 16:07:42 bjornw - * softirq_active/mask -> softirq_pending only - * - * Revision 1.30 2001/07/05 01:03:32 hp - * - include asm/errno.h to get ENOSYS. - * - Use ENOSYS, not local constant LENOSYS; tweak comments. - * - Explain why .include, not #include is used. - * - Make oops-register-dump if watchdog bits and it's not expected. - * - Don't jsr, use jump _hard_reset_now, and skip spurious nop. - * - Use correct section attribute for section .rodata. - * - Adjust sys_ni_syscall fill number. - * - * Revision 1.29 2001/06/25 14:07:00 hp - * Fix review comment. - * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of - * magic numbers. Add comment that -traditional must not be used. - * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation. - * Correct and update comment. - * * Makefile (.S.o): Don't use -traditional. Add comment why the - * toplevel rule can't be used (now that there's a reason). - * - * Revision 1.28 2001/06/21 02:00:40 hp - * * entry.S: Include asm/unistd.h. - * (_sys_call_table): Use section .rodata, not .data. - * (_kernel_thread): Move from... - * * process.c: ... here. - * * entryoffsets.c (VAL): Break out from... - * (OF): Use VAL. - * (LCLONE_VM): New asmified value from CLONE_VM. - * - * Revision 1.27 2001/05/29 11:25:27 markusl - * In case of "spurious_interrupt", do hard_reset instead of hanging system in a loop... - * - * Revision 1.26 2001/05/15 15:46:03 bjornw - * Include config.h now that we use some CONFIG_ options - * - * Revision 1.25 2001/05/15 05:38:47 hp - * Tweaked code in _ret_from_sys_call - * - * Revision 1.24 2001/05/15 05:27:49 hp - * Save r9 in r1 over function call rather than on stack. - * - * Revision 1.23 2001/05/15 05:10:00 hp - * Generate entry.S structure offsets from C - * - * Revision 1.22 2001/04/17 13:58:39 orjanf - * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. - * - * Revision 1.21 2001/04/17 11:33:29 orjanf - * Updated according to review: - * * Included asm/sv_addr_ag.h to get macro for internal register. - * * Corrected comment regarding system call argument passing. - * * Removed comment about instruction being in a delay slot. - * * Added comment about SYMBOL_NAME macro. - * - * Revision 1.20 2001/04/12 08:51:07 hp - * - Add entry for sys_fcntl64. In fact copy last piece from i386 including ... - * - .rept to fill table to safe state with sys_ni_syscall. - * - * Revision 1.19 2001/04/04 09:43:32 orjanf - * * Moved do_sigtrap from traps.c to entry.S. - * * LTASK_PID need not be global anymore. - * - * Revision 1.18 2001/03/26 09:25:02 markusl - * Updated after review, should now handle USB interrupts correctly. - * - * Revision 1.17 2001/03/21 16:12:55 bjornw - * * Always make room for the cpu status record in the frame, in order to - * use the same framelength and layout for both mmu busfaults and normal - * irqs. No need to check for the explicit CRIS_FRAME_FIXUP type anymore. - * * Fixed bug with using addq for popping the stack in the epilogue - it - * destroyed the flag register. Use instructions that don't affect the - * flag register instead. - * * Removed write to R_PORT_PA_DATA during spurious_interrupt - * - * Revision 1.16 2001/03/20 19:43:02 bjornw - * * Get rid of esp0 setting - * * Give a 7th argument to a systemcall - the stackframe - * - * Revision 1.15 2001/03/05 13:14:30 bjornw - * Spelling fix - * - * Revision 1.14 2001/02/23 08:36:36 perf - * New ABI; syscallnr=r9, arg5=mof, arg6=srp. - * Corrected tracesys call check. - * - * Revision 1.13 2001/02/15 08:40:55 perf - * H-P by way of perf; - * - (_system_call): Don't read system call function address into r1. - * - (RBFExit): There is no such thing as a null pop. Adjust sp by addq. - * - (_system_call): Don't use r10 and don't save and restore it. - * - (THREAD_ESP0): New constant. - * - (_system_call): Inline set_esp0. - * - * Revision 1.12 2001/01/31 17:56:25 orjanf - * Added definition of LTASK_PID and made it global. - * - * Revision 1.11 2001/01/10 21:13:29 bjornw - * SYMBOL_NAME is defined incorrectly for the compiler options we currently use - * - * Revision 1.10 2000/12/18 23:47:56 bjornw - * * Added syscall trace support (ptrace), completely untested of course - * * Removed redundant check for NULL entries in syscall_table - * - * Revision 1.9 2000/11/21 16:40:51 bjornw - * * New frame type used when an SBFS frame needs to be popped without - * actually restarting the instruction - * * Enable interrupts in signal_return (they did so in x86, I hope it's a good - * idea) - * - * Revision 1.8 2000/11/17 16:53:35 bjornw - * Added detection of frame-type in Rexit, so that mmu_bus_fault can - * use ret_from_intr in the return-path to check for signals (like SEGV) - * and other foul things that might have occurred during the fault. - * - * Revision 1.7 2000/10/06 15:04:28 bjornw - * Include mof in register savings - * - * Revision 1.6 2000/09/12 16:02:44 bjornw - * Linux-2.4.0-test7 derived updates - * - * Revision 1.5 2000/08/17 15:35:15 bjornw - * 2.4.0-test6 changed local_irq_count and friends API - * - * Revision 1.4 2000/08/02 13:59:30 bjornw - * Removed olduname and uname from the syscall list - * - * Revision 1.3 2000/07/31 13:32:58 bjornw - * * Export ret_from_intr - * * _resume updated (prev/last tjohejsan) - * * timer_interrupt obsolete - * * SIGSEGV detection in mmu_bus_fault temporarily disabled - * - * */ /* @@ -1167,9 +924,11 @@ sys_call_table: .long sys_epoll_pwait .long sys_utimensat /* 320 */ .long sys_signalfd - .long sys_ni_syscall + .long sys_timerfd_create .long sys_eventfd .long sys_fallocate + .long sys_timerfd_settime /* 325 */ + .long sys_timerfd_gettime /* * NOTE!! This doesn't have to be exact - we just have diff --git a/arch/cris/arch-v10/kernel/fasttimer.c b/arch/cris/arch-v10/kernel/fasttimer.c index c1a3a2100ee..31ff35cff02 100644 --- a/arch/cris/arch-v10/kernel/fasttimer.c +++ b/arch/cris/arch-v10/kernel/fasttimer.c @@ -31,15 +31,12 @@ #define DEBUG_LOG_INCLUDED #define FAST_TIMER_LOG -//#define FAST_TIMER_TEST +/* #define FAST_TIMER_TEST */ #define FAST_TIMER_SANITY_CHECKS #ifdef FAST_TIMER_SANITY_CHECKS -#define SANITYCHECK(x) x static int sanity_failed; -#else -#define SANITYCHECK(x) #endif #define D1(x) @@ -226,23 +223,19 @@ void start_one_shot_timer(struct fast_timer *t, do_gettimeofday_fast(&t->tv_set); tmp = fast_timer_list; - SANITYCHECK({ /* Check so this is not in the list already... */ - while (tmp != NULL) - { - if (tmp == t) - { - printk(KERN_WARNING - "timer name: %s data: 0x%08lX already in list!\n", name, data); - sanity_failed++; - goto done; - } - else - { - tmp = tmp->next; - } - } - tmp = fast_timer_list; - }); +#ifdef FAST_TIMER_SANITY_CHECKS + /* Check so this is not in the list already... */ + while (tmp != NULL) { + if (tmp == t) { + printk(KERN_WARNING "timer name: %s data: " + "0x%08lX already in list!\n", name, data); + sanity_failed++; + goto done; + } else + tmp = tmp->next; + } + tmp = fast_timer_list; +#endif t->delay_us = delay_us; t->function = function; diff --git a/arch/cris/arch-v10/kernel/head.S b/arch/cris/arch-v10/kernel/head.S index d946d8b8d27..96344afc4eb 100644 --- a/arch/cris/arch-v10/kernel/head.S +++ b/arch/cris/arch-v10/kernel/head.S @@ -1,186 +1,10 @@ -/* $Id: head.S,v 1.10 2005/06/20 05:12:54 starvik Exp $ - * +/* * Head of the kernel - alter with care * * Copyright (C) 2000, 2001 Axis Communications AB * * Authors: Bjorn Wesen (bjornw@axis.com) * - * $Log: head.S,v $ - * Revision 1.10 2005/06/20 05:12:54 starvik - * Remove unnecessary diff to kernel.org tree - * - * Revision 1.9 2004/12/13 12:21:51 starvik - * Added I/O and DMA allocators from Linux 2.4 - * - * Revision 1.8 2004/11/22 11:41:14 starvik - * Kernel command line may be supplied to kernel. Not used by Axis but may - * be used by customers. - * - * Revision 1.7 2004/05/14 07:58:01 starvik - * Merge of changes from 2.4 - * - * Revision 1.6 2003/04/28 05:31:46 starvik - * Added section attributes - * - * Revision 1.5 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/*.c - * - * Revision 1.4 2002/11/07 09:00:44 starvik - * Names changed for init sections - * init_task_union -> init_thread_union - * - * Revision 1.3 2002/02/05 15:38:23 bjornw - * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it... - * - * Revision 1.2 2001/12/18 13:35:19 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.43 2001/11/08 15:09:43 starvik - * Only start MII clock if Ethernet is configured - * - * Revision 1.42 2001/11/08 14:37:34 starvik - * Start MII clock early to make sure that it is running at tranceiver reset - * - * Revision 1.41 2001/10/29 14:55:58 pkj - * Corrected pa$r0 to par0. - * - * Revision 1.40 2001/10/03 14:59:57 pkj - * Added support for resetting the Bluetooth hardware. - * - * Revision 1.39 2001/10/01 14:45:03 bjornw - * Removed underscores and added register prefixes - * - * Revision 1.38 2001/09/21 07:14:11 jonashg - * Made root filesystem (cramfs) use mtdblock driver when booting from flash. - * - * Revision 1.37 2001/09/11 13:44:29 orjanf - * Decouple usage of serial ports for debug and kgdb. - * - * Revision 1.36 2001/06/29 12:39:31 pkj - * Added support for mirroring the first flash to just below the - * second one, to make them look consecutive to cramfs. - * - * Revision 1.35 2001/06/25 14:07:00 hp - * Fix review comment. - * * head.S: Use IO_STATE, IO_FIELD and IO_MASK constructs instead of - * magic numbers. Add comment that -traditional must not be used. - * * entry.S (SYMBOL_NAME): Change redefinition to use ## concatenation. - * Correct and update comment. - * * Makefile (.S.o): Don't use -traditional. Add comment why the - * toplevel rule can't be used (now that there's a reason). - * - * Revision 1.34 2001/05/15 07:08:14 hp - * Tweak "notice" to reflect that both r8 r9 are used - * - * Revision 1.33 2001/05/15 06:40:05 hp - * Put bulk of code in .text.init, data in .data.init - * - * Revision 1.32 2001/05/15 06:18:56 hp - * Execute review comment: s/bcc/bhs/g; s/bcs/blo/g - * - * Revision 1.31 2001/05/15 06:08:40 hp - * Add sentence about autodetecting the bit31-MMU-bug - * - * Revision 1.30 2001/05/15 06:00:05 hp - * Update comment: LOW_MAP is not forced on xsim anymore. - * - * Revision 1.29 2001/04/18 12:51:59 orjanf - * * Reverted review change regarding the use of bcs/bcc. - * * Removed non-working LED-clearing code. - * - * Revision 1.28 2001/04/17 13:58:39 orjanf - * * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. - * - * Revision 1.27 2001/04/17 11:42:35 orjanf - * Changed according to review: - * * Added comment explaining memory map bug. - * * Changed bcs and bcc to blo and bhs, respectively. - * * Removed mentioning of Stallone and Olga boards. - * - * Revision 1.26 2001/04/06 12:31:07 jonashg - * Check for cramfs in flash before RAM instead of RAM before flash. - * - * Revision 1.25 2001/04/04 06:23:53 starvik - * Initialize DRAM if not already initialized - * - * Revision 1.24 2001/04/03 11:12:00 starvik - * Removed dram init (done by rescue or etrax100boot - * Corrected include - * - * Revision 1.23 2001/04/03 09:53:03 starvik - * Include hw_settings.S - * - * Revision 1.22 2001/03/26 14:23:26 bjornw - * Namechange of some config options - * - * Revision 1.21 2001/03/08 12:14:41 bjornw - * * Config name for ETRAX IDE was renamed - * * Removed G27 auto-setting when JULIETTE is chosen (need to make this - * a new config option later) - * - * Revision 1.20 2001/02/23 12:47:56 bjornw - * MMU regs during LOW_MAP updated to reflect a newer reality - * - * Revision 1.19 2001/02/19 11:12:07 bjornw - * Changed comment header format - * - * Revision 1.18 2001/02/15 07:25:38 starvik - * Added support for synchronous serial ports - * - * Revision 1.17 2001/02/08 15:53:13 starvik - * Last commit removed some important ifdefs - * - * Revision 1.16 2001/02/08 15:20:38 starvik - * Include dram_init.S as inline - * - * Revision 1.15 2001/01/29 18:12:01 bjornw - * Corrected some comments - * - * Revision 1.14 2001/01/29 13:11:29 starvik - * Include dram_init.S (with DRAM/SDRAM initialization) - * - * Revision 1.13 2001/01/23 14:54:57 markusl - * Updated for USB - * i.e. added r_gen_config settings - * - * Revision 1.12 2001/01/19 16:16:29 perf - * Added temporary mapping of 0x0c->0x0c to avoid flash loading confusion. - * Renamed serial options from ETRAX100 to ETRAX. - * - * Revision 1.11 2001/01/16 16:31:38 bjornw - * * Changed name and semantics of running_from_flash to romfs_in_flash, - * set by head.S to indicate to setup.c whether there is a cramfs image - * after the kernels BSS or not. Should work for all three boot-cases - * (DRAM with cramfs in DRAM, DRAM with cramfs in flash (compressed boot), - * and flash with cramfs in flash) - * - * Revision 1.10 2001/01/16 14:12:21 bjornw - * * Check for cramfs start passed in r9 from the decompressor, if all other - * cramfs options fail (if we boot from DRAM but don't find a cramfs image - * after the kernel in DRAM, it is probably still in the flash) - * * Check magic in cramfs detection when booting from flash directly - * - * Revision 1.9 2001/01/15 17:17:02 bjornw - * * Corrected the code that detects the cramfs lengths - * * Added a comment saying that the above does not work due to other - * reasons.. - * - * Revision 1.8 2001/01/15 16:27:51 jonashg - * Made boot after flashing work. - * * end destination is __vmlinux_end in RAM. - * * _romfs_start moved because of virtual memory. - * - * Revision 1.7 2000/11/21 13:55:29 bjornw - * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type - * - * Revision 1.6 2000/10/06 12:36:55 bjornw - * Forgot swapper_pg_dir when changing memory map.. - * - * Revision 1.5 2000/10/04 16:49:30 bjornw - * * Fixed memory mapping in LX - * * Check for cramfs instead of romfs - * */ #define ASSEMBLER_MACROS_ONLY @@ -595,11 +419,17 @@ no_command_line: moveq 0,$r0 + ;; Select or disable serial port 2 +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + or.d IO_STATE (R_GEN_CONFIG, ser2, select),$r0 +#else + or.d IO_STATE (R_GEN_CONFIG, ser2, disable),$r0 +#endif + ;; Init interfaces (disable them). or.d IO_STATE (R_GEN_CONFIG, scsi0, disable) \ | IO_STATE (R_GEN_CONFIG, ata, disable) \ | IO_STATE (R_GEN_CONFIG, par0, disable) \ - | IO_STATE (R_GEN_CONFIG, ser2, disable) \ | IO_STATE (R_GEN_CONFIG, mio, disable) \ | IO_STATE (R_GEN_CONFIG, scsi1, disable) \ | IO_STATE (R_GEN_CONFIG, scsi0w, disable) \ @@ -801,6 +631,41 @@ no_command_line: | IO_STATE (R_SERIAL1_TR_CTRL, tr_bitnr, tr_8bit),$r0 move.b $r0,[R_SERIAL1_TR_CTRL] +#ifdef CONFIG_ETRAX_SERIAL_PORT2 + ;; setup the serial port 2 at 115200 baud for debug purposes + + moveq IO_STATE (R_SERIAL2_XOFF, tx_stop, enable) \ + | IO_STATE (R_SERIAL2_XOFF, auto_xoff, disable) \ + | IO_FIELD (R_SERIAL2_XOFF, xoff_char, 0),$r0 + move.d $r0,[R_SERIAL2_XOFF] + + ; 115.2kbaud for both transmit and receive + move.b IO_STATE (R_SERIAL2_BAUD, tr_baud, c115k2Hz) \ + | IO_STATE (R_SERIAL2_BAUD, rec_baud, c115k2Hz),$r0 + move.b $r0,[R_SERIAL2_BAUD] + + ; Set up and enable the serial2 receiver. + move.b IO_STATE (R_SERIAL2_REC_CTRL, dma_err, stop) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_enable, enable) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rts_, active) \ + | IO_STATE (R_SERIAL2_REC_CTRL, sampling, middle) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_stick_par, normal) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_par, even) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_par_en, disable) \ + | IO_STATE (R_SERIAL2_REC_CTRL, rec_bitnr, rec_8bit),$r0 + move.b $r0,[R_SERIAL2_REC_CTRL] + + ; Set up and enable the serial2 transmitter. + move.b IO_FIELD (R_SERIAL2_TR_CTRL, txd, 0) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_enable, enable) \ + | IO_STATE (R_SERIAL2_TR_CTRL, auto_cts, disabled) \ + | IO_STATE (R_SERIAL2_TR_CTRL, stop_bits, one_bit) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_stick_par, normal) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_par, even) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_par_en, disable) \ + | IO_STATE (R_SERIAL2_TR_CTRL, tr_bitnr, tr_8bit),$r0 + move.b $r0,[R_SERIAL2_TR_CTRL] +#endif #ifdef CONFIG_ETRAX_SERIAL_PORT3 ;; setup the serial port 3 at 115200 baud for debug purposes diff --git a/arch/cris/arch-v10/kernel/io_interface_mux.c b/arch/cris/arch-v10/kernel/io_interface_mux.c index f3b327d4ed9..add98e0941b 100644 --- a/arch/cris/arch-v10/kernel/io_interface_mux.c +++ b/arch/cris/arch-v10/kernel/io_interface_mux.c @@ -1,10 +1,9 @@ /* IO interface mux allocator for ETRAX100LX. - * Copyright 2004, Axis Communications AB - * $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $ + * Copyright 2004-2007, Axis Communications AB */ -/* C.f. ETRAX100LX Designer's Reference 20.9 */ +/* C.f. ETRAX100LX Designer's Reference chapter 19.9 */ #include <linux/kernel.h> #include <linux/slab.h> @@ -45,17 +44,39 @@ struct watcher struct if_group { enum io_if_group group; - unsigned char used; - enum cris_io_interface owner; + /* name - the name of the group 'A' to 'F' */ + char *name; + /* used - a bit mask of all pins in the group in the order listed + * in the tables in 19.9.1 to 19.9.6. Note that no + * distinction is made between in, out and in/out pins. */ + unsigned int used; }; struct interface { enum cris_io_interface ioif; + /* name - the name of the interface */ + char *name; + /* groups - OR'ed together io_if_group flags describing what pin groups + * the interface uses pins in. */ unsigned char groups; + /* used - set when the interface is allocated. */ unsigned char used; char *owner; + /* group_a through group_f - bit masks describing what pins in the + * pin groups the interface uses. */ + unsigned int group_a; + unsigned int group_b; + unsigned int group_c; + unsigned int group_d; + unsigned int group_e; + unsigned int group_f; + + /* gpio_g_in, gpio_g_out, gpio_b - bit masks telling what pins in the + * GPIO ports the interface uses. This could be reconstucted using + * the group_X masks and a table of what pins the GPIO ports use, + * but that would be messy. */ unsigned int gpio_g_in; unsigned int gpio_g_out; unsigned char gpio_b; @@ -64,26 +85,32 @@ struct interface static struct if_group if_groups[6] = { { .group = group_a, + .name = "A", .used = 0, }, { .group = group_b, + .name = "B", .used = 0, }, { .group = group_c, + .name = "C", .used = 0, }, { .group = group_d, + .name = "D", .used = 0, }, { .group = group_e, + .name = "E", .used = 0, }, { .group = group_f, + .name = "F", .used = 0, } }; @@ -94,14 +121,32 @@ static struct interface interfaces[] = { /* Begin Non-multiplexed interfaces */ { .ioif = if_eth, + .name = "ethernet", .groups = 0, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0, .gpio_g_out = 0, .gpio_b = 0 }, { .ioif = if_serial_0, + .name = "serial_0", .groups = 0, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0, .gpio_g_out = 0, .gpio_b = 0 @@ -109,172 +154,385 @@ static struct interface interfaces[] = { /* End Non-multiplexed interfaces */ { .ioif = if_serial_1, + .name = "serial_1", .groups = group_e, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x00 }, { .ioif = if_serial_2, + .name = "serial_2", .groups = group_b, + + .group_a = 0, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x000000c0, .gpio_g_out = 0x000000c0, .gpio_b = 0x00 }, { .ioif = if_serial_3, + .name = "serial_3", .groups = group_c, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x00 }, { .ioif = if_sync_serial_1, - .groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3 - can be used simultaneously */ + .name = "sync_serial_1", + .groups = group_e | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0x10, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x10 }, { .ioif = if_sync_serial_3, + .name = "sync_serial_3", .groups = group_c | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0, + .group_e = 0, + .group_f = 0x80, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x80 }, { .ioif = if_shared_ram, + .name = "shared_ram", .groups = group_a, + + .group_a = 0x7f8ff, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3e, .gpio_g_out = 0x0000ff38, .gpio_b = 0x00 }, { .ioif = if_shared_ram_w, + .name = "shared_ram_w", .groups = group_a | group_d, + + .group_a = 0x7f8ff, + .group_b = 0, + .group_c = 0, + .group_d = 0xff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00ffff3e, .gpio_g_out = 0x00ffff38, .gpio_b = 0x00 }, { .ioif = if_par_0, + .name = "par_0", .groups = group_a, + + .group_a = 0x7fbff, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3e, .gpio_g_out = 0x0000ff3e, .gpio_b = 0x00 }, { .ioif = if_par_1, + .name = "par_1", .groups = group_d, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0x7feff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x3eff0000, .gpio_g_out = 0x3eff0000, .gpio_b = 0x00 }, { .ioif = if_par_w, + .name = "par_w", .groups = group_a | group_d, + + .group_a = 0x7fbff, + .group_b = 0, + .group_c = 0, + .group_d = 0xff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00ffff3e, .gpio_g_out = 0x00ffff3e, .gpio_b = 0x00 }, { .ioif = if_scsi8_0, - .groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1 - can be used simultaneously */ + .name = "scsi8_0", + .groups = group_a | group_b | group_f, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0x10, + .gpio_g_in = 0x0000ffff, .gpio_g_out = 0x0000ffff, .gpio_b = 0x10 }, { .ioif = if_scsi8_1, - .groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1 - can be used simultaneously */ + .name = "scsi8_1", + .groups = group_c | group_d | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0x0f, + .group_d = 0x7ffff, + .group_e = 0, + .group_f = 0x80, + .gpio_g_in = 0xffff0000, .gpio_g_out = 0xffff0000, .gpio_b = 0x80 }, { .ioif = if_scsi_w, + .name = "scsi_w", .groups = group_a | group_b | group_d | group_f, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0, + .group_d = 0x601ff, + .group_e = 0, + .group_f = 0x90, + .gpio_g_in = 0x01ffffff, .gpio_g_out = 0x07ffffff, .gpio_b = 0x80 }, { .ioif = if_ata, + .name = "ata", .groups = group_a | group_b | group_c | group_d, + + .group_a = 0x7ffff, + .group_b = 0x0f, + .group_c = 0x0f, + .group_d = 0x7cfff, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xf9ffffff, .gpio_g_out = 0xffffffff, .gpio_b = 0x80 }, { .ioif = if_csp, - .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .name = "csp", + .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0xfc, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0xfc }, { .ioif = if_i2c, - .groups = group_f, /* if_csp and if_i2c can be used simultaneously */ + .name = "i2c", + .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0x03, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x03 }, { .ioif = if_usb_1, + .name = "usb_1", .groups = group_e | group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x0f, + .group_f = 0x2c, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x2c }, { .ioif = if_usb_2, + .name = "usb_2", .groups = group_d, - .gpio_g_in = 0x0e000000, - .gpio_g_out = 0x3c000000, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0x33e00, + .group_f = 0, + + .gpio_g_in = 0x3e000000, + .gpio_g_out = 0x0c000000, .gpio_b = 0x00 }, /* GPIO pins */ { .ioif = if_gpio_grp_a, + .name = "gpio_a", .groups = group_a, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x0000ff3f, .gpio_g_out = 0x0000ff3f, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_b, + .name = "gpio_b", .groups = group_b, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x000000c0, .gpio_g_out = 0x000000c0, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_c, + .name = "gpio_c", .groups = group_c, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0xc0000000, .gpio_g_out = 0xc0000000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_d, + .name = "gpio_d", .groups = group_d, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x3fff0000, .gpio_g_out = 0x3fff0000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_e, + .name = "gpio_e", .groups = group_e, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0x00 }, { .ioif = if_gpio_grp_f, + .name = "gpio_f", .groups = group_f, + + .group_a = 0, + .group_b = 0, + .group_c = 0, + .group_d = 0, + .group_e = 0, + .group_f = 0, + .gpio_g_in = 0x00000000, .gpio_g_out = 0x00000000, .gpio_b = 0xff @@ -284,11 +542,13 @@ static struct interface interfaces[] = { static struct watcher *watchers = NULL; +/* The pins that are free to use in the GPIO ports. */ static unsigned int gpio_in_pins = 0xffffffff; static unsigned int gpio_out_pins = 0xffffffff; static unsigned char gpio_pb_pins = 0xff; static unsigned char gpio_pa_pins = 0xff; +/* Identifiers for the owners of the GPIO pins. */ static enum cris_io_interface gpio_pa_owners[8]; static enum cris_io_interface gpio_pb_owners[8]; static enum cris_io_interface gpio_pg_owners[32]; @@ -338,13 +598,15 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id struct if_group *grp; unsigned char group_set; unsigned long flags; + int res = 0; (void)cris_io_interface_init(); DBG(printk("cris_request_io_interface(%d, \"%s\")\n", ioif, device_id)); if ((ioif >= if_max_interfaces) || (ioif < 0)) { - printk(KERN_CRIT "cris_request_io_interface: Bad interface %u submitted for %s\n", + printk(KERN_CRIT "cris_request_io_interface: Bad interface " + "%u submitted for %s\n", ioif, device_id); return -EINVAL; @@ -353,59 +615,69 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id local_irq_save(flags); if (interfaces[ioif].used) { - local_irq_restore(flags); - printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n", + printk(KERN_CRIT "cris_io_interface: Cannot allocate interface " + "%s for %s, in use by %s\n", + interfaces[ioif].name, device_id, interfaces[ioif].owner); - return -EBUSY; + res = -EBUSY; + goto exit; } - /* Check that all required groups are free before allocating, */ + /* Check that all required pins in the used groups are free + * before allocating. */ group_set = interfaces[ioif].groups; while (NULL != (grp = get_group(group_set))) { - if (grp->used) { - if (grp->group == group_f) { - if ((if_sync_serial_1 == ioif) || - (if_sync_serial_3 == ioif)) { - if ((grp->owner != if_sync_serial_1) && - (grp->owner != if_sync_serial_3)) { - local_irq_restore(flags); - return -EBUSY; - } - } else if ((if_scsi8_0 == ioif) || - (if_scsi8_1 == ioif)) { - if ((grp->owner != if_scsi8_0) && - (grp->owner != if_scsi8_1)) { - local_irq_restore(flags); - return -EBUSY; - } - } - } else { - local_irq_restore(flags); - return -EBUSY; - } + unsigned int if_group_use = 0; + + switch (grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); } + + if (if_group_use & grp->used) { + printk(KERN_INFO "cris_request_io_interface: group " + "%s needed by %s not available\n", + grp->name, interfaces[ioif].name); + res = -EBUSY; + goto exit; + } + group_set = clear_group_from_set(group_set, grp); } /* Are the required GPIO pins available too? */ - if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) || - ((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) || - ((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) { - local_irq_restore(flags); - printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n", - ioif); - return -EBUSY; - } - - /* All needed I/O pins and pin groups are free, allocate. */ - group_set = interfaces[ioif].groups; - while (NULL != (grp = get_group(group_set))) { - grp->used = 1; - grp->owner = ioif; - group_set = clear_group_from_set(group_set, grp); + if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != + interfaces[ioif].gpio_g_in) || + ((interfaces[ioif].gpio_g_out & gpio_out_pins) != + interfaces[ioif].gpio_g_out) || + ((interfaces[ioif].gpio_b & gpio_pb_pins) != + interfaces[ioif].gpio_b)) { + printk(KERN_CRIT "cris_request_io_interface: Could not get " + "required pins for interface %u\n", ioif); + res = -EBUSY; + goto exit; } + /* Check which registers need to be reconfigured. */ gens = genconfig_shadow; gens_ii = gen_config_ii_shadow; @@ -495,9 +767,43 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id set_gen_config = 0; break; default: - panic("cris_request_io_interface: Bad interface %u submitted for %s\n", - ioif, - device_id); + printk(KERN_INFO "cris_request_io_interface: Bad interface " + "%u submitted for %s\n", + ioif, device_id); + res = -EBUSY; + goto exit; + } + + /* All needed I/O pins and pin groups are free, allocate. */ + group_set = interfaces[ioif].groups; + while (NULL != (grp = get_group(group_set))) { + unsigned int if_group_use = 0; + + switch (grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); + } + grp->used |= if_group_use; + + group_set = clear_group_from_set(group_set, grp); } interfaces[ioif].used = 1; @@ -516,25 +822,28 @@ int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id *R_GEN_CONFIG_II = gen_config_ii_shadow; } - DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", - gpio_in_pins, gpio_out_pins, gpio_pb_pins)); - DBG(printk("grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", - interfaces[ioif].gpio_g_in, - interfaces[ioif].gpio_g_out, - interfaces[ioif].gpio_b)); + DBG(printk(KERN_DEBUG "GPIO pins: available before: " + "g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + DBG(printk(KERN_DEBUG + "grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + interfaces[ioif].gpio_g_in, + interfaces[ioif].gpio_g_out, + interfaces[ioif].gpio_b)); gpio_in_pins &= ~interfaces[ioif].gpio_g_in; gpio_out_pins &= ~interfaces[ioif].gpio_g_out; gpio_pb_pins &= ~interfaces[ioif].gpio_b; - DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n", - gpio_in_pins, gpio_out_pins, gpio_pb_pins)); + DBG(printk(KERN_DEBUG "GPIO pins: available after: " + "g_in=0x%08x g_out=0x%08x pb=0x%02x\n", + gpio_in_pins, gpio_out_pins, gpio_pb_pins)); +exit: local_irq_restore(flags); - - notify_watchers(); - - return 0; + if (res == 0) + notify_watchers(); + return res; } @@ -560,43 +869,35 @@ void cris_free_io_interface(enum cris_io_interface ioif) } group_set = interfaces[ioif].groups; while (NULL != (grp = get_group(group_set))) { - if (grp->group == group_f) { - switch (ioif) - { - case if_sync_serial_1: - if ((grp->owner == if_sync_serial_1) && - interfaces[if_sync_serial_3].used) { - grp->owner = if_sync_serial_3; - } else - grp->used = 0; - break; - case if_sync_serial_3: - if ((grp->owner == if_sync_serial_3) && - interfaces[if_sync_serial_1].used) { - grp->owner = if_sync_serial_1; - } else - grp->used = 0; - break; - case if_scsi8_0: - if ((grp->owner == if_scsi8_0) && - interfaces[if_scsi8_1].used) { - grp->owner = if_scsi8_1; - } else - grp->used = 0; - break; - case if_scsi8_1: - if ((grp->owner == if_scsi8_1) && - interfaces[if_scsi8_0].used) { - grp->owner = if_scsi8_0; - } else - grp->used = 0; - break; - default: - grp->used = 0; - } - } else { - grp->used = 0; + unsigned int if_group_use = 0; + + switch (grp->group) { + case group_a: + if_group_use = interfaces[ioif].group_a; + break; + case group_b: + if_group_use = interfaces[ioif].group_b; + break; + case group_c: + if_group_use = interfaces[ioif].group_c; + break; + case group_d: + if_group_use = interfaces[ioif].group_d; + break; + case group_e: + if_group_use = interfaces[ioif].group_e; + break; + case group_f: + if_group_use = interfaces[ioif].group_f; + break; + default: + BUG_ON(1); } + + if ((grp->used & if_group_use) != if_group_use) + BUG_ON(1); + grp->used = grp->used & ~if_group_use; + group_set = clear_group_from_set(group_set, grp); } interfaces[ioif].used = 0; diff --git a/arch/cris/arch-v10/kernel/irq.c b/arch/cris/arch-v10/kernel/irq.c index e06ab0050d3..65ed803dae6 100644 --- a/arch/cris/arch-v10/kernel/irq.c +++ b/arch/cris/arch-v10/kernel/irq.c @@ -1,5 +1,4 @@ -/* $Id: irq.c,v 1.4 2005/01/04 12:22:28 starvik Exp $ - * +/* * linux/arch/cris/kernel/irq.c * * Copyright (c) 2000-2002 Axis Communications AB @@ -18,10 +17,6 @@ #include <linux/kernel.h> #include <linux/init.h> -/* From kgdb.c. */ -extern void kgdb_init(void); -extern void breakpoint(void); - #define mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr)); #define unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr)); diff --git a/arch/cris/arch-v10/kernel/kgdb.c b/arch/cris/arch-v10/kernel/kgdb.c index 77f4b142372..a3ca5515074 100644 --- a/arch/cris/arch-v10/kernel/kgdb.c +++ b/arch/cris/arch-v10/kernel/kgdb.c @@ -17,66 +17,8 @@ *! Jun 17 1999 Hendrik Ruijter Added gdb 4.18 support. 'X', 'qC' and 'qL'. *! Jul 21 1999 Bjorn Wesen eLinux port *! -*! $Log: kgdb.c,v $ -*! Revision 1.6 2005/01/14 10:12:17 starvik -*! KGDB on separate port. -*! Console fixes from 2.4. -*! -*! Revision 1.5 2004/10/07 13:59:08 starvik -*! Corrected call to set_int_vector -*! -*! Revision 1.4 2003/04/09 05:20:44 starvik -*! Merge of Linux 2.5.67 -*! -*! Revision 1.3 2003/01/21 19:11:08 starvik -*! Modified include path for new dir layout -*! -*! Revision 1.2 2002/11/19 14:35:24 starvik -*! Changes from linux 2.4 -*! Changed struct initializer syntax to the currently preferred notation -*! -*! Revision 1.1 2001/12/17 13:59:27 bjornw -*! Initial revision -*! -*! Revision 1.6 2001/10/09 13:10:03 matsfg -*! Added $ on registers and removed some underscores -*! -*! Revision 1.5 2001/04/17 13:58:39 orjanf -*! * Renamed CONFIG_KGDB to CONFIG_ETRAX_KGDB. -*! -*! Revision 1.4 2001/02/23 13:45:19 bjornw -*! config.h check -*! -*! Revision 1.3 2001/01/31 18:08:23 orjanf -*! Removed kgdb_handle_breakpoint from being the break 8 handler. -*! -*! Revision 1.2 2001/01/12 14:22:25 orjanf -*! Updated kernel debugging support to work with ETRAX 100LX. -*! -*! Revision 1.1 2000/07/10 16:25:21 bjornw -*! Initial revision -*! -*! Revision 1.1.1.1 1999/12/03 14:57:31 bjornw -*! * Initial version of arch/cris, the latest CRIS architecture with an MMU. -*! Mostly copied from arch/etrax100 with appropriate renames of files. -*! The mm/ subdir is copied from arch/i386. -*! This does not compile yet at all. -*! -*! -*! Revision 1.4 1999/07/22 17:25:25 bjornw -*! Dont wait for + in putpacket if we havent hit the initial breakpoint yet. Added a kgdb_init function which sets up the break and irq vectors. -*! -*! Revision 1.3 1999/07/21 19:51:18 bjornw -*! Check if the interrupting char is a ctrl-C, ignore otherwise. -*! -*! Revision 1.2 1999/07/21 18:09:39 bjornw -*! Ported to eLinux architecture, and added some kgdb documentation. -*! -*! *!--------------------------------------------------------------------------- *! -*! $Id: kgdb.c,v 1.6 2005/01/14 10:12:17 starvik Exp $ -*! *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN *! *!**************************************************************************/ diff --git a/arch/cris/arch-v10/kernel/process.c b/arch/cris/arch-v10/kernel/process.c index 1a3760c94f8..53117f07cc1 100644 --- a/arch/cris/arch-v10/kernel/process.c +++ b/arch/cris/arch-v10/kernel/process.c @@ -1,5 +1,4 @@ -/* $Id: process.c,v 1.12 2004/12/27 11:18:32 starvik Exp $ - * +/* * linux/arch/cris/kernel/process.c * * Copyright (C) 1995 Linus Torvalds diff --git a/arch/cris/arch-v10/kernel/ptrace.c b/arch/cris/arch-v10/kernel/ptrace.c index b570ae9b6ca..ee505b2eb4d 100644 --- a/arch/cris/arch-v10/kernel/ptrace.c +++ b/arch/cris/arch-v10/kernel/ptrace.c @@ -65,6 +65,7 @@ void ptrace_disable(struct task_struct *child) { /* Todo - pending singlesteps? */ + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); } /* diff --git a/arch/cris/arch-v10/kernel/shadows.c b/arch/cris/arch-v10/kernel/shadows.c index 326178aef6e..2454a0b02f5 100644 --- a/arch/cris/arch-v10/kernel/shadows.c +++ b/arch/cris/arch-v10/kernel/shadows.c @@ -1,5 +1,4 @@ -/* $Id: shadows.c,v 1.2 2004/12/13 12:21:51 starvik Exp $ - * +/* * Various shadow registers. Defines for these are in include/asm-etrax100/io.h */ diff --git a/arch/cris/arch-v10/kernel/traps.c b/arch/cris/arch-v10/kernel/traps.c index 4becc1bcced..9eada5d8893 100644 --- a/arch/cris/arch-v10/kernel/traps.c +++ b/arch/cris/arch-v10/kernel/traps.c @@ -1,13 +1,10 @@ -/* $Id: traps.c,v 1.4 2005/04/24 18:47:55 starvik Exp $ +/* + * Helper functions for trap handlers * - * linux/arch/cris/arch-v10/traps.c + * Copyright (C) 2000-2007, Axis Communications AB. * - * Heler functions for trap handlers - * - * Copyright (C) 2000-2002 Axis Communications AB - * - * Authors: Bjorn Wesen - * Hans-Peter Nilsson + * Authors: Bjorn Wesen + * Hans-Peter Nilsson * */ @@ -15,124 +12,119 @@ #include <asm/uaccess.h> #include <asm/arch/sv_addr_ag.h> -extern int raw_printk(const char *fmt, ...); - -void -show_registers(struct pt_regs * regs) +void +show_registers(struct pt_regs *regs) { - /* We either use rdusp() - the USP register, which might not - correspond to the current process for all cases we're called, - or we use the current->thread.usp, which is not up to date for - the current process. Experience shows we want the USP - register. */ + /* + * It's possible to use either the USP register or current->thread.usp. + * USP might not correspond to the current process for all cases this + * function is called, and current->thread.usp isn't up to date for the + * current process. Experience shows that using USP is the way to go. + */ unsigned long usp = rdusp(); - raw_printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", - regs->irp, regs->srp, regs->dccr, usp, regs->mof ); - raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n", + regs->irp, regs->srp, regs->dccr, usp, regs->mof); + + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", regs->r0, regs->r1, regs->r2, regs->r3); - raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", regs->r4, regs->r5, regs->r6, regs->r7); - raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", regs->r8, regs->r9, regs->r10, regs->r11); - raw_printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n", - regs->r12, regs->r13, regs->orig_r10, regs); - raw_printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); - raw_printk("Process %s (pid: %d, stackpage=%08lx)\n", + + printk("r12: %08lx r13: %08lx oR10: %08lx sp: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, (long unsigned)regs); + + printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE); + + printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, (unsigned long)current); /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (! user_mode(regs)) { - int i; + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + int i; - show_stack(NULL, (unsigned long*)usp); + show_stack(NULL, (unsigned long *)usp); - /* Dump kernel stack if the previous dump wasn't one. */ + /* + * If the previous stack-dump wasn't a kernel one, dump the + * kernel stack now. + */ if (usp != 0) - show_stack (NULL, NULL); - - raw_printk("\nCode: "); - if(regs->irp < PAGE_OFFSET) - goto bad; - - /* Often enough the value at regs->irp does not point to - the interesting instruction, which is most often the - _previous_ instruction. So we dump at an offset large - enough that instruction decoding should be in sync at - the interesting point, but small enough to fit on a row - (sort of). We point out the regs->irp location in a - ksymoops-friendly way by wrapping the byte for that - address in parentheses. */ - for(i = -12; i < 12; i++) - { - unsigned char c; - if(__get_user(c, &((unsigned char*)regs->irp)[i])) { -bad: - raw_printk(" Bad IP value."); - break; - } + show_stack(NULL, NULL); + + printk("\nCode: "); + + if (regs->irp < PAGE_OFFSET) + goto bad_value; + + /* + * Quite often the value at regs->irp doesn't point to the + * interesting instruction, which often is the previous + * instruction. So dump at an offset large enough that the + * instruction decoding should be in sync at the interesting + * point, but small enough to fit on a row. The regs->irp + * location is pointed out in a ksymoops-friendly way by + * wrapping the byte for that address in parenthesises. + */ + for (i = -12; i < 12; i++) { + unsigned char c; + + if (__get_user(c, &((unsigned char *)regs->irp)[i])) { +bad_value: + printk(" Bad IP value."); + break; + } if (i == 0) - raw_printk("(%02x) ", c); + printk("(%02x) ", c); else - raw_printk("%02x ", c); - } - raw_printk("\n"); - } + printk("%02x ", c); + } + printk("\n"); + } } -/* Called from entry.S when the watchdog has bitten - * We print out something resembling an oops dump, and if - * we have the nice doggy development flag set, we halt here - * instead of rebooting. - */ - -extern void reset_watchdog(void); -extern void stop_watchdog(void); - - void -watchdog_bite_hook(struct pt_regs *regs) +arch_enable_nmi(void) { -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - local_irq_disable(); - stop_watchdog(); - show_registers(regs); - while(1) /* nothing */; -#else - show_registers(regs); -#endif + asm volatile ("setf m"); } -/* This is normally the 'Oops' routine */ -void -die_if_kernel(const char * str, struct pt_regs * regs, long err) +extern void (*nmi_handler)(struct pt_regs *); +void handle_nmi(struct pt_regs *regs) { - if(user_mode(regs)) - return; - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* This printout might take too long and trigger the - * watchdog normally. If we're in the nice doggy - * development mode, stop the watchdog during printout. - */ - stop_watchdog(); -#endif - - raw_printk("%s: %04lx\n", str, err & 0xffff); - - show_registers(regs); - -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif - do_exit(SIGSEGV); + if (nmi_handler) + nmi_handler(regs); + + /* Wait until nmi is no longer active. (We enable NMI immediately after + returning from this function, and we don't want it happening while + exiting from the NMI interrupt handler.) */ + while (*R_IRQ_MASK0_RD & IO_STATE(R_IRQ_MASK0_RD, nmi_pin, active)) + ; } -void arch_enable_nmi(void) +#ifdef CONFIG_DEBUG_BUGVERBOSE +void +handle_BUG(struct pt_regs *regs) { - asm volatile("setf m"); + struct bug_frame f; + unsigned char c; + unsigned long irp = regs->irp; + + if (__copy_from_user(&f, (const void __user *)(irp - 8), sizeof f)) + return; + if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC) + return; + if (__get_user(c, f.filename)) + f.filename = "<bad filename>"; + + printk("kernel BUG at %s:%d!\n", f.filename, f.line); } +#endif diff --git a/arch/cris/arch-v10/lib/checksum.S b/arch/cris/arch-v10/lib/checksum.S index 85c48f0a9ec..7d552f4bd5a 100644 --- a/arch/cris/arch-v10/lib/checksum.S +++ b/arch/cris/arch-v10/lib/checksum.S @@ -1,4 +1,4 @@ -/* $Id: checksum.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* * A fast checksum routine using movem * Copyright (c) 1998-2001 Axis Communications AB * @@ -61,8 +61,6 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords ax addq 0,$r12 - ax ; do it again, since we might have generated a carry - addq 0,$r12 subq 10*4,$r11 bge _mloop @@ -88,10 +86,6 @@ _word_loop: lsrq 16,$r13 ; r13 = checksum >> 16 and.d $r9,$r12 ; checksum = checksum & 0xffff add.d $r13,$r12 ; checksum += r13 - move.d $r12,$r13 ; do the same again, maybe we got a carry last add - lsrq 16,$r13 - and.d $r9,$r12 - add.d $r13,$r12 _no_fold: cmpq 2,$r11 diff --git a/arch/cris/arch-v10/lib/checksumcopy.S b/arch/cris/arch-v10/lib/checksumcopy.S index 35cbffb306f..540db8a5f84 100644 --- a/arch/cris/arch-v10/lib/checksumcopy.S +++ b/arch/cris/arch-v10/lib/checksumcopy.S @@ -1,4 +1,4 @@ -/* $Id: checksumcopy.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $ +/* * A fast checksum+copy routine using movem * Copyright (c) 1998, 2001 Axis Communications AB * @@ -67,8 +67,6 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords ax addq 0,$r13 - ax ; do it again, since we might have generated a carry - addq 0,$r13 subq 10*4,$r12 bge _mloop @@ -91,10 +89,6 @@ _word_loop: lsrq 16,$r9 ; r0 = checksum >> 16 and.d 0xffff,$r13 ; checksum = checksum & 0xffff add.d $r9,$r13 ; checksum += r0 - move.d $r13,$r9 ; do the same again, maybe we got a carry last add - lsrq 16,$r9 - and.d 0xffff,$r13 - add.d $r9,$r13 _no_fold: cmpq 2,$r12 diff --git a/arch/cris/arch-v10/lib/dram_init.S b/arch/cris/arch-v10/lib/dram_init.S index 6a6bdfd6984..b9190ff7d0a 100644 --- a/arch/cris/arch-v10/lib/dram_init.S +++ b/arch/cris/arch-v10/lib/dram_init.S @@ -1,5 +1,4 @@ -/* $Id: dram_init.S,v 1.4 2003/09/22 09:21:59 starvik Exp $ - * +/* * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files * @@ -8,60 +7,7 @@ * * Copyright (C) 2000, 2001 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) - * - * $Log: dram_init.S,v $ - * Revision 1.4 2003/09/22 09:21:59 starvik - * Decompresser is linked to 0x407xxxxx and sdram commands are at 0x000xxxxx - * so we need to mask off 12 bits. - * - * Revision 1.3 2003/03/31 09:38:37 starvik - * Corrected calculation of end of sdram init commands - * - * Revision 1.2 2002/11/19 13:33:29 starvik - * Changes from Linux 2.4 - * - * Revision 1.13 2002/10/30 07:42:28 starvik - * Always read SDRAM command sequence from flash - * - * Revision 1.12 2002/08/09 11:37:37 orjanf - * Added double initialization work-around for Samsung SDRAMs. - * - * Revision 1.11 2002/06/04 11:43:21 starvik - * Check if mrs_data is specified in kernelconfig (necessary for MCM) - * - * Revision 1.10 2001/10/04 12:00:21 martinnn - * Added missing underscores. - * - * Revision 1.9 2001/10/01 14:47:35 bjornw - * Added register prefixes and removed underscores - * - * Revision 1.8 2001/05/15 07:12:45 hp - * Copy warning from head.S about r8 and r9 - * - * Revision 1.7 2001/04/18 12:05:39 bjornw - * Fixed comments, and explicitly include config.h to be sure its there - * - * Revision 1.6 2001/04/10 06:20:16 starvik - * Delay should be 200us, not 200ns - * - * Revision 1.5 2001/04/09 06:01:13 starvik - * Added support for 100 MHz SDRAMs - * - * Revision 1.4 2001/03/26 14:24:01 bjornw - * Namechange of some config options - * - * Revision 1.3 2001/03/23 08:29:41 starvik - * Corrected calculation of mrs_data - * - * Revision 1.2 2001/02/08 15:20:00 starvik - * Corrected SDRAM initialization - * Should now be included as inline - * - * Revision 1.1 2001/01/29 13:08:02 starvik - * Initial version - * This file should be included from all assembler files that needs to - * initialize DRAM/SDRAM. + * Authors: Mikael Starvik (starvik@axis.com) * */ diff --git a/arch/cris/arch-v10/lib/old_checksum.c b/arch/cris/arch-v10/lib/old_checksum.c index 497634a6482..1734b467efa 100644 --- a/arch/cris/arch-v10/lib/old_checksum.c +++ b/arch/cris/arch-v10/lib/old_checksum.c @@ -1,5 +1,4 @@ -/* $Id: old_checksum.c,v 1.3 2003/10/27 08:04:32 starvik Exp $ - * +/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. diff --git a/arch/cris/arch-v10/mm/fault.c b/arch/cris/arch-v10/mm/fault.c index fe2615022b9..65504fd8092 100644 --- a/arch/cris/arch-v10/mm/fault.c +++ b/arch/cris/arch-v10/mm/fault.c @@ -4,10 +4,10 @@ * Low level bus fault handler * * - * Copyright (C) 2000, 2001 Axis Communications AB + * Copyright (C) 2000-2007 Axis Communications AB + * + * Authors: Bjorn Wesen * - * Authors: Bjorn Wesen - * */ #include <linux/mm.h> @@ -60,7 +60,7 @@ handle_mmu_bus_fault(struct pt_regs *regs) #ifdef DEBUG page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); - inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); + inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); index = IO_EXTRACT(R_TLB_SELECT, index, select); #endif miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); @@ -84,12 +84,13 @@ handle_mmu_bus_fault(struct pt_regs *regs) local_irq_disable(); pmd = (pmd_t *)(pgd + pgd_index(address)); if (pmd_none(*pmd)) - return; + goto exit; pte = *pte_offset_kernel(pmd, address); if (!pte_present(pte)) - return; + goto exit; *R_TLB_SELECT = select; *R_TLB_HI = cause; *R_TLB_LO = pte_val(pte); +exit: local_irq_restore(flags); } diff --git a/arch/cris/arch-v10/mm/tlb.c b/arch/cris/arch-v10/mm/tlb.c index 7d9fec88dee..6baf5bd209e 100644 --- a/arch/cris/arch-v10/mm/tlb.c +++ b/arch/cris/arch-v10/mm/tlb.c @@ -4,8 +4,8 @@ * Low level TLB handling * * - * Copyright (C) 2000-2002 Axis Communications AB - * + * Copyright (C) 2000-2007 Axis Communications AB + * * Authors: Bjorn Wesen (bjornw@axis.com) * */ @@ -39,7 +39,7 @@ flush_tlb_all(void) unsigned long flags; /* the vpn of i & 0xf is so we dont write similar TLB entries - * in the same 4-way entry group. details.. + * in the same 4-way entry group. details... */ local_irq_save(flags); @@ -47,7 +47,7 @@ flush_tlb_all(void) *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | IO_STATE(R_TLB_LO, valid, no ) | IO_STATE(R_TLB_LO, kernel,no ) | @@ -71,10 +71,10 @@ flush_tlb_mm(struct mm_struct *mm) if(page_id == NO_CONTEXT) return; - + /* mark the TLB entries that match the page_id as invalid. * here we could also check the _PAGE_GLOBAL bit and NOT flush - * global pages. is it worth the extra I/O ? + * global pages. is it worth the extra I/O ? */ local_irq_save(flags); @@ -83,7 +83,7 @@ flush_tlb_mm(struct mm_struct *mm) if (IO_EXTRACT(R_TLB_HI, page_id, *R_TLB_HI) == page_id) { *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); - + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | IO_STATE(R_TLB_LO, valid, no ) | IO_STATE(R_TLB_LO, kernel,no ) | @@ -96,9 +96,7 @@ flush_tlb_mm(struct mm_struct *mm) /* invalidate a single page */ -void -flush_tlb_page(struct vm_area_struct *vma, - unsigned long addr) +void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { struct mm_struct *mm = vma->vm_mm; int page_id = mm->context.page_id; @@ -113,7 +111,7 @@ flush_tlb_page(struct vm_area_struct *vma, addr &= PAGE_MASK; /* perhaps not necessary */ /* invalidate those TLB entries that match both the mm context - * and the virtual address requested + * and the virtual address requested */ local_irq_save(flags); @@ -125,7 +123,7 @@ flush_tlb_page(struct vm_area_struct *vma, (tlb_hi & PAGE_MASK) == addr) { *R_TLB_HI = IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | addr; /* same addr as before works. */ - + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | IO_STATE(R_TLB_LO, valid, no ) | IO_STATE(R_TLB_LO, kernel,no ) | @@ -144,7 +142,7 @@ dump_tlb_all(void) { int i; unsigned long flags; - + printk("TLB dump. LO is: pfn | reserved | global | valid | kernel | we |\n"); local_save_flags(flags); @@ -172,27 +170,29 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm) /* called in schedule() just before actually doing the switch_to */ -void -switch_mm(struct mm_struct *prev, struct mm_struct *next, - struct task_struct *tsk) +void switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk) { - /* make sure we have a context */ + if (prev != next) { + /* make sure we have a context */ + get_mmu_context(next); - get_mmu_context(next); + /* remember the pgd for the fault handlers + * this is similar to the pgd register in some other CPU's. + * we need our own copy of it because current and active_mm + * might be invalid at points where we still need to derefer + * the pgd. + */ - /* remember the pgd for the fault handlers - * this is similar to the pgd register in some other CPU's. - * we need our own copy of it because current and active_mm - * might be invalid at points where we still need to derefer - * the pgd. - */ + per_cpu(current_pgd, smp_processor_id()) = next->pgd; - per_cpu(current_pgd, smp_processor_id()) = next->pgd; + /* switch context in the MMU */ - /* switch context in the MMU */ - - D(printk("switching mmu_context to %d (%p)\n", next->context, next)); + D(printk(KERN_DEBUG "switching mmu_context to %d (%p)\n", + next->context, next)); - *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context.page_id); + *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, + page_id, next->context.page_id); + } } diff --git a/arch/cris/arch-v32/Kconfig b/arch/cris/arch-v32/Kconfig index d8acaa920e1..005ed2b3f7f 100644 --- a/arch/cris/arch-v32/Kconfig +++ b/arch/cris/arch-v32/Kconfig @@ -1,27 +1,73 @@ if ETRAX_ARCH_V32 +source arch/cris/arch-v32/mach-fs/Kconfig +source arch/cris/arch-v32/mach-a3/Kconfig + +source drivers/cpufreq/Kconfig + config ETRAX_DRAM_VIRTUAL_BASE hex depends on ETRAX_ARCH_V32 default "c0000000" -config ETRAX_LED1G - string "First green LED bit" +choice + prompt "Nbr of Ethernet LED groups" depends on ETRAX_ARCH_V32 + default ETRAX_NBR_LED_GRP_ONE + help + Select how many Ethernet LED groups that can be used. Usually one per Ethernet + interface is a good choice. + +config ETRAX_NBR_LED_GRP_ZERO + bool "Use zero LED groups" + help + Select this if you do not want any Ethernet LEDs. + +config ETRAX_NBR_LED_GRP_ONE + bool "Use one LED group" + help + Select this if you want one Ethernet LED group. This LED group + can be used for one or more Ethernet interfaces. However, it is + recomended that each Ethernet interface use a dedicated LED group. + +config ETRAX_NBR_LED_GRP_TWO + bool "Use two LED groups" + help + Select this if you want two Ethernet LED groups. This is the + best choice if you have more than one Ethernet interface and + would like to have separate LEDs for the interfaces. + +endchoice + +config ETRAX_LED_G_NET0 + string "Ethernet LED group 0 green LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA3" help - Bit to use for the first green LED (network LED). - Most Axis products use bit A3 here. + Bit to use for the green LED in Ethernet LED group 0. -config ETRAX_LED1R - string "First red LED bit" - depends on ETRAX_ARCH_V32 +config ETRAX_LED_R_NET0 + string "Ethernet LED group 0 red LED bit" + depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO) default "PA4" help - Bit to use for the first red LED (network LED). - Most Axis products use bit A4 here. + Bit to use for the red LED in Ethernet LED group 0. -config ETRAX_LED2G +config ETRAX_LED_G_NET1 + string "Ethernet group 1 green LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the green LED in Ethernet LED group 1. + +config ETRAX_LED_R_NET1 + string "Ethernet group 1 red LED bit" + depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO + default "" + help + Bit to use for the red LED in Ethernet LED group 1. + +config ETRAX_V32_LED2G string "Second green LED bit" depends on ETRAX_ARCH_V32 default "PA5" @@ -29,7 +75,7 @@ config ETRAX_LED2G Bit to use for the first green LED (status LED). Most Axis products use bit A5 here. -config ETRAX_LED2R +config ETRAX_V32_LED2R string "Second red LED bit" depends on ETRAX_ARCH_V32 default "PA6" @@ -37,7 +83,7 @@ config ETRAX_LED2R Bit to use for the first red LED (network LED). Most Axis products use bit A6 here. -config ETRAX_LED3G +config ETRAX_V32_LED3G string "Third green LED bit" depends on ETRAX_ARCH_V32 default "PA7" @@ -45,7 +91,7 @@ config ETRAX_LED3G Bit to use for the first green LED (drive/power LED). Most Axis products use bit A7 here. -config ETRAX_LED3R +config ETRAX_V32_LED3R string "Third red LED bit" depends on ETRAX_ARCH_V32 default "PA7" @@ -54,39 +100,6 @@ config ETRAX_LED3R Most Axis products use bit A7 here. choice - prompt "Product debug-port" - depends on ETRAX_ARCH_V32 - default ETRAX_DEBUG_PORT0 - -config ETRAX_DEBUG_PORT0 - bool "Serial-0" - help - Choose a serial port for the ETRAX debug console. Default to - port 0. - -config ETRAX_DEBUG_PORT1 - bool "Serial-1" - help - Use serial port 1 for the console. - -config ETRAX_DEBUG_PORT2 - bool "Serial-2" - help - Use serial port 2 for the console. - -config ETRAX_DEBUG_PORT3 - bool "Serial-3" - help - Use serial port 3 for the console. - -config ETRAX_DEBUG_PORT_NULL - bool "disabled" - help - Disable serial-port debugging. - -endchoice - -choice prompt "Kernel GDB port" depends on ETRAX_KGDB default ETRAX_KGDB_PORT0 @@ -95,25 +108,11 @@ choice not be enabled under Drivers for built-in interfaces (as it has its own initialization code) and should not be the same as the debug port. -config ETRAX_KGDB_PORT0 - bool "Serial-0" - help - Use serial port 0 for kernel debugging. - -config ETRAX_KGDB_PORT1 - bool "Serial-1" - help - Use serial port 1 for kernel debugging. - -config ETRAX_KGDB_PORT2 - bool "Serial-2" - help - Use serial port 2 for kernel debugging. - -config ETRAX_KGDB_PORT3 - bool "Serial-3" +config ETRAX_KGDB_PORT4 + bool "Serial-4" + depends on ETRAX_SERIAL_PORTS = 5 help - Use serial port 3 for kernel debugging. + Use serial port 4 for kernel debugging. endchoice diff --git a/arch/cris/arch-v32/boot/Makefile b/arch/cris/arch-v32/boot/Makefile index 26f293ab961..3f91349c5f1 100644 --- a/arch/cris/arch-v32/boot/Makefile +++ b/arch/cris/arch-v32/boot/Makefile @@ -1,14 +1,21 @@ # # arch/cris/arch-v32/boot/Makefile # -target = $(target_boot_dir) -src = $(src_boot_dir) -zImage: compressed/vmlinuz +OBJCOPY = objcopy-cris +OBJCOPYFLAGS = -O binary -R .note -R .comment -compressed/vmlinuz: $(objtree)/vmlinux - @$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz +subdir- := compressed rescue +targets := Image -clean: - rm -f zImage tools/build compressed/vmlinux.out - @$(MAKE) -f $(src)/compressed/Makefile clean +$(obj)/Image: vmlinux FORCE + $(call if_changed,objcopy) + @echo ' Kernel: $@ is ready' + +$(obj)/compressed/vmlinux: $(obj)/Image FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ + $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin + +$(obj)/zImage: $(obj)/compressed/vmlinux + @cp $< $@ + @echo ' Kernel: $@ is ready' diff --git a/arch/cris/arch-v32/boot/compressed/Makefile b/arch/cris/arch-v32/boot/compressed/Makefile index 609692f9d5e..2c8c2c3039c 100644 --- a/arch/cris/arch-v32/boot/compressed/Makefile +++ b/arch/cris/arch-v32/boot/compressed/Makefile @@ -1,41 +1,30 @@ # -# lx25/arch/cris/arch-v32/boot/compressed/Makefile +# arch/cris/arch-v32/boot/compressed/Makefile # -# create a compressed vmlinux image from the original vmlinux files and romfs -# - -target = $(target_compressed_dir) -src = $(src_compressed_dir) CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) -CFLAGS = -O2 +asflags-y += -I $(srctree)/include/asm/mach/ -I $(srctree)/include/asm/arch +ccflags-y += -O2 -I $(srctree)/include/asm/mach/ -I $(srctree)/include/asm/arch LD = gcc-cris -mlinux -march=v32 -nostdlib +ldflags-y += -T $(obj)/decompress.ld +obj-y = head.o misc.o +OBJECTS = $(obj)/head.o $(obj)/misc.o OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss -OBJECTS = $(target)/head.o $(target)/misc.o - -# files to compress -SYSTEM = $(objtree)/vmlinux.bin - -all: vmlinuz - -$(target)/decompress.bin: $(OBJECTS) - $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS) - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin -$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin - cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz - rm -f piggy.img - cp $(objtree)/vmlinuz $(src) +quiet_cmd_image = BUILD $@ +cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@ -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $@ +targets := vmlinux piggy.gz decompress.o decompress.bin -# gzip the kernel image +$(obj)/decompress.o: $(OBJECTS) FORCE + $(call if_changed,ld) -piggy.img: $(SYSTEM) - cat $(SYSTEM) | gzip -f -9 > piggy.img +$(obj)/decompress.bin: $(obj)/decompress.o FORCE + $(call if_changed,objcopy) -clean: - rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS) +$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE + $(call if_changed,image) +$(obj)/piggy.gz: $(obj)/../Image FORCE + $(call if_changed,gzip) diff --git a/arch/cris/arch-v32/boot/compressed/README b/arch/cris/arch-v32/boot/compressed/README index e33691d15c5..182c5d75784 100644 --- a/arch/cris/arch-v32/boot/compressed/README +++ b/arch/cris/arch-v32/boot/compressed/README @@ -1,6 +1,5 @@ Creation of the self-extracting compressed kernel image (vmlinuz) ----------------------------------------------------------------- -$Id: README,v 1.1 2003/08/21 09:37:03 johana Exp $ This can be slightly confusing because it's a process with many steps. diff --git a/arch/cris/arch-v32/boot/compressed/head.S b/arch/cris/arch-v32/boot/compressed/head.S index 34cea10a899..f86208caf32 100644 --- a/arch/cris/arch-v32/boot/compressed/head.S +++ b/arch/cris/arch-v32/boot/compressed/head.S @@ -2,13 +2,12 @@ * Code that sets up the DRAM registers, calls the * decompressor to unpack the piggybacked kernel, and jumps. * - * Copyright (C) 1999 - 2003, Axis Communications AB + * Copyright (C) 1999 - 2006, Axis Communications AB */ #define ASSEMBLER_MACROS_ONLY -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/gio_defs_asm.h> -#include <asm/arch/hwregs/asm/config_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <asm/arch/mach/startup.inc> #define RAM_INIT_MAGIC 0x56902387 #define COMMAND_LINE_MAGIC 0x87109563 @@ -22,114 +21,49 @@ start: di ;; Start clocks for used blocks. - move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 - move.d [$r1], $r0 - or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ - REG_STATE(config, rw_clk_ctrl, bif, yes) | \ - REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 - move.d $r0, [$r1] - - ;; If booting from NAND flash we first have to copy some - ;; data from NAND flash to internal RAM to get the code - ;; that initializes the SDRAM. Lets copy 20 KB. This - ;; code executes at 0x38010000 if booting from NAND and - ;; we are guaranted that at least 0x200 bytes are good so - ;; lets start from there. The first 8192 bytes in the nand - ;; flash is spliced with zeroes and is thus 16384 bytes. - move.d 0x38010200, $r10 - move.d 0x14200, $r11 ; Start offset in NAND flash 0x10200 + 16384 - move.d 0x5000, $r12 ; Length of copy - - ;; Before this code the tools add a partitiontable so the PC - ;; has an offset from the linked address. -offset1: - lapcq ., $r13 ; get PC - add.d first_copy_complete-offset1, $r13 - -#include "../../lib/nand_init.S" - -first_copy_complete: - ;; Initialze the DRAM registers. + START_CLOCKS + + ;; Initialize the DRAM registers. cmp.d RAM_INIT_MAGIC, $r8 ; Already initialized? beq dram_init_finished nop -#include "../../lib/dram_init.S" +#include "../../mach/dram_init.S" dram_init_finished: - lapcq ., $r13 ; get PC - add.d second_copy_complete-dram_init_finished, $r13 - - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne second_copy_complete ; No NAND boot - nop - - ;; Copy 2MB from NAND flash to SDRAM (at 2-4MB into the SDRAM) - move.d 0x40204000, $r10 - move.d 0x8000, $r11 - move.d 0x200000, $r12 - ba copy_nand_to_ram - nop -second_copy_complete: - - ;; Initiate the PA port. - move.d CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0 - move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r1 - move.d $r0, [$r1] - - move.d CONFIG_ETRAX_DEF_GIO_PA_OE, $r0 - move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r1 - move.d $r0, [$r1] + GIO_INIT ;; Setup the stack to a suitably high address. ;; We assume 8 MB is the minimum DRAM and put ;; the SP at the top for now. move.d 0x40800000, $sp - ;; Figure out where the compressed piggyback image is - ;; in the flash (since we wont try to copy it to DRAM - ;; before unpacking). It is at _edata, but in flash. + ;; Figure out where the compressed piggyback image is. + ;; It is either in [NOR] flash (we don't want to copy it + ;; to DRAM before unpacking), or copied to DRAM + ;; by the [NAND] flash boot loader. + ;; The piggyback image is at _edata, but relative to where the + ;; image is actually located in memory, not where it is linked + ;; (the decompressor is linked at 0x40700000+ and runs there). ;; Use (_edata - herami) as offset to the current PC. - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - beq hereami2 - nop hereami: lapcq ., $r5 ; get PC and.d 0x7fffffff, $r5 ; strip any non-cache bit - move.d $r5, $r0 ; save for later - flash address of 'herami' + move.d $r5, $r0 ; source address of 'herami' add.d _edata, $r5 sub.d hereami, $r5 ; r5 = flash address of '_edata' move.d hereami, $r1 ; destination - ba 2f - nop -hereami2: - lapcq ., $r5 ; get PC - and.d 0x00ffffff, $r5 ; strip any non-cache bit - move.d $r5, $r6 - or.d 0x40200000, $r6 - move.d $r6, $r0 ; save for later - flash address of 'herami' - add.d _edata, $r5 - sub.d hereami2, $r5 ; r5 = flash address of '_edata' - add.d 0x40200000, $r5 - move.d hereami2, $r1 ; destination -2: + ;; Copy text+data to DRAM move.d _edata, $r2 ; end destination -1: move.w [$r0+], $r3 - move.w $r3, [$r1+] - cmp.d $r2, $r1 +1: move.w [$r0+], $r3 ; from herami+ source + move.w $r3, [$r1+] ; to hereami+ destination (linked address) + cmp.d $r2, $r1 ; finish when destination == _edata bcs 1b nop - move.d input_data, $r0 ; for the decompressor move.d $r5, [$r0] ; for the decompressor @@ -144,16 +78,24 @@ hereami2: nop ;; Save command line magic and address. - move.d _cmd_line_magic, $r12 - move.d $r10, [$r12] - move.d _cmd_line_addr, $r12 - move.d $r11, [$r12] + move.d _cmd_line_magic, $r0 + move.d $r10, [$r0] + move.d _cmd_line_addr, $r0 + move.d $r11, [$r0] + + ;; Save boot source indicator + move.d _boot_source, $r0 + move.d $r12, [$r0] ;; Do the decompression and save compressed size in _inptr jsr decompress_kernel nop + ;; Restore boot source indicator + move.d _boot_source, $r12 + move.d [$r12], $r12 + ;; Restore command line magic and address. move.d _cmd_line_magic, $r10 move.d [$r10], $r10 @@ -166,11 +108,10 @@ hereami2: move.d [$r0], $r9 ; flash address of compressed kernel move.d inptr, $r0 add.d [$r0], $r9 ; size of compressed kernel - cmp.d 0x40200000, $r9 - blo enter_kernel - nop - sub.d 0x40200000, $r9 - add.d 0x4000, $r9 + cmp.d 0x40000000, $r9 ; image in DRAM ? + blo enter_kernel ; no, must be [NOR] flash, jump + nop ; delay slot + and.d 0x001fffff, $r9 ; assume compressed kernel was < 2M enter_kernel: ;; Enter the decompressed kernel @@ -186,7 +127,7 @@ _cmd_line_magic: .dword 0 _cmd_line_addr: .dword 0 -is_nand_boot: - .dword 0 +_boot_source: + .dword 0 -#include "../../lib/hw_settings.S" +#include "../../mach/hw_settings.S" diff --git a/arch/cris/arch-v32/boot/compressed/misc.c b/arch/cris/arch-v32/boot/compressed/misc.c index 0169ba1ca9c..55b2695c5d7 100644 --- a/arch/cris/arch-v32/boot/compressed/misc.c +++ b/arch/cris/arch-v32/boot/compressed/misc.c @@ -1,8 +1,6 @@ /* * misc.c * - * $Id: misc.c,v 1.8 2005/04/24 18:34:29 starvik Exp $ - * * This is a collection of several routines from gzip-1.0.3 * adapted for Linux. * @@ -22,9 +20,13 @@ #include <linux/types.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/ser_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/ser_defs.h> +#include <hwregs/pinmux_defs.h> +#ifdef CONFIG_CRIS_MACH_ARTPEC3 +#include <hwregs/clkgen_defs.h> +#endif /* * gzip declarations @@ -85,7 +87,6 @@ static unsigned outcnt = 0; /* bytes in output buffer */ # define Tracecv(c,x) #endif -static int fill_inbuf(void); static void flush_window(void); static void error(char *m); static void gzip_mark(void **); @@ -186,6 +187,8 @@ memset(void* s, int c, size_t n) char *ss = (char*)s; for (i=0;i<n;i++) ss[i] = c; + + return s; } void* @@ -196,6 +199,8 @@ memcpy(void* __dest, __const void* __src, char *d = (char *)__dest, *s = (char *)__src; for (i=0;i<__n;i++) d[i] = s[i]; + + return __dest; } /* =========================================================================== @@ -225,15 +230,15 @@ flush_window() static void error(char *x) { - puts("\n\n"); + puts("\r\n\n"); puts(x); - puts("\n\n -- System halted\n"); + puts("\r\n\n -- System halted\n"); while(1); /* Halt */ } void -setup_normal_output_buffer() +setup_normal_output_buffer(void) { output_data = (char *)KERNEL_LOAD_ADR; } @@ -262,15 +267,17 @@ serial_setup(reg_scope_instances regi_ser) rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div); tr_ctrl.stop_bits = 1; /* 2 stop bits. */ + tr_ctrl.en = 1; /* enable transmitter */ + rec_ctrl.en = 1; /* enabler receiver */ /* - * The baudrate setup is a bit fishy, but in the end the transceiver is - * set to 4800 and the receiver to 115200. The magic value is - * 29.493 MHz. + * The baudrate setup used to be a bit fishy, but now transmitter and + * receiver are both set to the intended baud rate, 115200. + * The magic value is 29.493 MHz. */ tr_ctrl.base_freq = regk_ser_f29_493; rec_ctrl.base_freq = regk_ser_f29_493; - tr_baud.div = (29493000 / 8) / 4800; + tr_baud.div = (29493000 / 8) / 115200; rec_baud.div = (29493000 / 8) / 115200; REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl); @@ -280,25 +287,52 @@ serial_setup(reg_scope_instances regi_ser) } void -decompress_kernel() +decompress_kernel(void) { char revision; - /* input_data is set in head.S */ - inbuf = input_data; +#if defined(CONFIG_ETRAX_DEBUG_PORT1) || \ + defined(CONFIG_ETRAX_DEBUG_PORT2) || \ + defined(CONFIG_ETRAX_DEBUG_PORT3) + reg_pinmux_rw_hwprot hwprot; + +#ifdef CONFIG_CRIS_MACH_ARTPEC3 + reg_clkgen_rw_clk_ctrl clk_ctrl; + + /* Enable corresponding clock region when serial 1..3 selected */ + + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); +#endif + + /* pinmux setup for ports 1..3 */ + hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); +#endif #ifdef CONFIG_ETRAX_DEBUG_PORT0 serial_setup(regi_ser0); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT1 + hwprot.ser1 = regk_pinmux_yes; serial_setup(regi_ser1); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT2 + hwprot.ser2 = regk_pinmux_yes; serial_setup(regi_ser2); #endif #ifdef CONFIG_ETRAX_DEBUG_PORT3 + hwprot.ser3 = regk_pinmux_yes; serial_setup(regi_ser3); #endif +#if defined(CONFIG_ETRAX_DEBUG_PORT1) || \ + defined(CONFIG_ETRAX_DEBUG_PORT2) || \ + defined(CONFIG_ETRAX_DEBUG_PORT3) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); +#endif + + /* input_data is set in head.S */ + inbuf = input_data; setup_normal_output_buffer(); @@ -307,11 +341,11 @@ decompress_kernel() __asm__ volatile ("move $vr,%0" : "=rm" (revision)); if (revision < 32) { - puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n"); + puts("You need an ETRAX FS to run Linux 2.6/crisv32.\r\n"); while(1); } - puts("Uncompressing Linux...\n"); + puts("Uncompressing Linux...\r\n"); gunzip(); - puts("Done. Now booting the kernel.\n"); + puts("Done. Now booting the kernel.\r\n"); } diff --git a/arch/cris/arch-v32/boot/rescue/Makefile b/arch/cris/arch-v32/boot/rescue/Makefile index f668a819872..c0987795dcb 100644 --- a/arch/cris/arch-v32/boot/rescue/Makefile +++ b/arch/cris/arch-v32/boot/rescue/Makefile @@ -1,36 +1,27 @@ # -# Makefile for rescue code +# Makefile for rescue (bootstrap) code # -target = $(target_rescue_dir) -src = $(src_rescue_dir) CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE) -CFLAGS = -O2 +ccflags-y += -O2 -I $(srctree)/include/asm/arch/mach/ \ + -I $(srctree)/include/asm/arch +asflags-y += -I $(srctree)/include/asm/arch/mach/ -I $(srctree)/include/asm/arch LD = gcc-cris -mlinux -march=v32 -nostdlib +ldflags-y += -T $(obj)/rescue.ld +LDPOSTFLAGS = -lgcc OBJCOPY = objcopy-cris OBJCOPYFLAGS = -O binary --remove-section=.bss +obj-$(CONFIG_ETRAX_AXISFLASHMAP) = head.o +OBJECT := $(obj)/head.o -all: $(target)/rescue.bin +targets := rescue.o rescue.bin -rescue: rescue.bin - # do nothing +quiet_cmd_ldlibgcc = LD $@ +cmd_ldlibgcc = $(LD) $(LDFLAGS) $(filter-out FORCE,$^) $(LDPOSTFLAGS) -o $@ -$(target)/rescue.bin: $(target) $(target)/head.o - $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o - $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin - cp -p $(target)/rescue.bin $(objtree) +$(obj)/rescue.o: $(OBJECTS) FORCE + $(call if_changed,ldlibgcc) -$(target): - mkdir -p $(target) - -$(target)/head.o: $(src)/head.S - $(CC) -D__ASSEMBLY__ -c $< -o $*.o - -clean: - rm -f $(target)/*.o $(target)/*.bin - -fastdep: - -modules: - -modules-install: +$(obj)/rescue.bin: $(obj)/rescue.o FORCE + $(call if_changed,objcopy) + cp -p $(obj)/rescue.bin $(objtree) diff --git a/arch/cris/arch-v32/boot/rescue/head.S b/arch/cris/arch-v32/boot/rescue/head.S index 8cdb4011bc1..5f846b7700a 100644 --- a/arch/cris/arch-v32/boot/rescue/head.S +++ b/arch/cris/arch-v32/boot/rescue/head.S @@ -1,38 +1,26 @@ -/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $ +/* + * Just get started by jumping to CONFIG_ETRAX_PTABLE_SECTOR to start + * kernel decompressor. + * + * In practice, this only works for NOR flash (or some convoluted RAM boot) + * and hence is not really useful for Artpec-3, so it's Etrax FS / NOR only. * - * This used to be the rescue code but now that is handled by the - * RedBoot based RFL instead. Nothing to see here, move along. */ -#include <asm/arch/hwregs/reg_map_asm.h> -#include <asm/arch/hwregs/config_defs_asm.h> +#include <mach/startup.inc> - .text +#ifdef CONFIG_ETRAX_AXISFLASHMAP - ;; Start clocks for used blocks. - move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 - move.d [$r1], $r0 - or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ - REG_STATE(config, rw_clk_ctrl, bif, yes) | \ - REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 - move.d $r0, [$r1] +;; Code - ;; Copy 68KB NAND flash to Internal RAM (if NAND boot) - move.d 0x38004000, $r10 - move.d 0x8000, $r11 - move.d 0x11000, $r12 - move.d copy_complete, $r13 - and.d 0x000fffff, $r13 - or.d 0x38000000, $r13 + .text +start: -#include "../../lib/nand_init.S" + ;; Start clocks for used blocks. + START_CLOCKS - ;; No NAND found move.d CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser + jump $r10 ; Jump to decompressor nop -copy_complete: - move.d 0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10 - jump $r10 ; Jump to decompresser - nop +#endif diff --git a/arch/cris/arch-v32/boot/rescue/rescue.ld b/arch/cris/arch-v32/boot/rescue/rescue.ld index 42b11aa122b..8ac646bc1a2 100644 --- a/arch/cris/arch-v32/boot/rescue/rescue.ld +++ b/arch/cris/arch-v32/boot/rescue/rescue.ld @@ -1,20 +1,43 @@ +/*#OUTPUT_FORMAT(elf32-us-cris) */ +OUTPUT_ARCH (crisv32) +/* Now that NAND support has been stripped, this file could be simplified, + * but it doesn't do any harm on the other hand so why bother. */ + MEMORY { - flash : ORIGIN = 0x00000000, - LENGTH = 0x00100000 + bootblk : ORIGIN = 0x38000000, + LENGTH = 0x00004000 + intmem : ORIGIN = 0x38004000, + LENGTH = 0x00005000 } SECTIONS { .text : { - stext = . ; + _stext = . ; *(.text) - etext = . ; - } > flash + *(.init.text) + *(.rodata) + *(.rodata.*) + _etext = . ; + } > bootblk .data : { *(.data) - edata = . ; - } > flash + _edata = . ; + } > bootblk + .bss : + { + _bss = . ; + *(.bss) + _end = ALIGN( 0x10 ) ; + } > intmem + + /* Get rid of stuff from EXPORT_SYMBOL(foo). */ + /DISCARD/ : + { + *(__ksymtab_strings) + *(__ksymtab) + } } diff --git a/arch/cris/arch-v32/drivers/Kconfig b/arch/cris/arch-v32/drivers/Kconfig index c329cce2a0c..2a92cb1886c 100644 --- a/arch/cris/arch-v32/drivers/Kconfig +++ b/arch/cris/arch-v32/drivers/Kconfig @@ -4,64 +4,102 @@ config ETRAX_ETHERNET bool "Ethernet support" depends on ETRAX_ARCH_V32 select NET_ETHERNET + select MII help This option enables the ETRAX FS built-in 10/100Mbit Ethernet controller. -config ETRAX_ETHERNET_HW_CSUM - bool "Hardware accelerated ethernet checksum and scatter/gather" +config ETRAX_NO_PHY + bool "PHY not present" depends on ETRAX_ETHERNET - depends on ETRAX_STREAMCOPROC - default y + default N help - Hardware acceleration of checksumming and scatter/gather + This option disables all MDIO communication with an ethernet + transceiver connected to the MII interface. This option shall + typically be enabled if the MII interface is connected to a + switch. This option should normally be disabled. If enabled, + speed and duplex will be locked to 100 Mbit and full duplex. config ETRAX_ETHERNET_IFACE0 depends on ETRAX_ETHERNET bool "Enable network interface 0" config ETRAX_ETHERNET_IFACE1 - depends on ETRAX_ETHERNET + depends on (ETRAX_ETHERNET && ETRAXFS) bool "Enable network interface 1 (uses DMA6 and DMA7)" +config ETRAX_ETHERNET_GBIT + depends on (ETRAX_ETHERNET && CRIS_MACH_ARTPEC3) + bool "Enable gigabit Ethernet support" + choice - prompt "Network LED behavior" - depends on ETRAX_ETHERNET - default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY + prompt "Eth0 led group" + depends on ETRAX_ETHERNET_IFACE0 + default ETRAX_ETH0_USE_LEDGRP0 -config ETRAX_NETWORK_LED_ON_WHEN_LINK - bool "LED_on_when_link" +config ETRAX_ETH0_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Use LED grp 0 for eth0 - Selecting LED_on_when_activity will light the LED only when - there is activity. - - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. +config ETRAX_ETH0_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth0 -config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY - bool "LED_on_when_activity" +config ETRAX_ETH0_USE_LEDGRPNULL + bool "Use no LEDs for eth0" help - Selecting LED_on_when_link will light the LED when there is a - connection and will flash off when there is activity. + Use no LEDs for eth0 +endchoice - Selecting LED_on_when_activity will light the LED only when - there is activity. +choice + prompt "Eth1 led group" + depends on ETRAX_ETHERNET_IFACE1 + default ETRAX_ETH1_USE_LEDGRP1 + +config ETRAX_ETH1_USE_LEDGRP0 + bool "Use LED grp 0" + depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 0 for eth1 - This setting will also affect the behaviour of other activity LEDs - e.g. Bluetooth. +config ETRAX_ETH1_USE_LEDGRP1 + bool "Use LED grp 1" + depends on ETRAX_NBR_LED_GRP_TWO + help + Use LED grp 1 for eth1 +config ETRAX_ETH1_USE_LEDGRPNULL + bool "Use no LEDs for eth1" + help + Use no LEDs for eth1 endchoice config ETRAXFS_SERIAL bool "Serial-port support" depends on ETRAX_ARCH_V32 + select SERIAL_CORE + select SERIAL_CORE_CONSOLE help Enables the ETRAX FS serial driver for ser0 (ttyS0) You probably want this enabled. +config ETRAX_RS485 + bool "RS-485 support" + depends on ETRAXFS_SERIAL + help + Enables support for RS-485 serial communication. + +config ETRAX_RS485_DISABLE_RECEIVER + bool "Disable serial receiver" + depends on ETRAX_RS485 + help + It is necessary to disable the serial receiver to avoid serial + loopback. Not all products are able to do this in software only. + config ETRAX_SERIAL_PORT0 bool "Serial port 0 enabled" depends on ETRAXFS_SERIAL @@ -72,50 +110,28 @@ config ETRAX_SERIAL_PORT0 ser0 can use dma4 or dma6 for output and dma5 or dma7 for input. choice - prompt "Ser0 DMA in channel " + prompt "Ser0 default port type " depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_NO_DMA_IN + default ETRAX_SERIAL_PORT0_TYPE_232 help - What DMA channel to use for ser0. - + Type of serial port. -config ETRAX_SERIAL_PORT0_NO_DMA_IN - bool "Ser0 uses no DMA for input" +config ETRAX_SERIAL_PORT0_TYPE_232 + bool "Ser0 is a RS-232 port" help - Do not use DMA for ser0 input. + Configure serial port 0 to be a RS-232 port. -config ETRAX_SERIAL_PORT0_DMA7_IN - bool "Ser0 uses DMA7 for input" - depends on ETRAX_SERIAL_PORT0 - help - Enables the DMA7 input channel for ser0 (ttyS0). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - -endchoice - -choice - prompt "Ser0 DMA out channel" - depends on ETRAX_SERIAL_PORT0 - default ETRAX_SERIAL_PORT0_NO_DMA_OUT - -config ETRAX_SERIAL_PORT0_NO_DMA_OUT - bool "Ser0 uses no DMA for output" +config ETRAX_SERIAL_PORT0_TYPE_485HD + bool "Ser0 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser0 output. + Configure serial port 0 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT0_DMA6_OUT - bool "Ser0 uses DMA6 for output" - depends on ETRAX_SERIAL_PORT0 +config ETRAX_SERIAL_PORT0_TYPE_485FD + bool "Ser0 is a full duplex RS-485 port" + depends on ETRAX_RS485 help - Enables the DMA6 output channel for ser0 (ttyS0). - If you do not enable DMA, an interrupt for each character will be - used when transmitting data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - + Configure serial port 0 to be a full duplex (four wires) RS-485 port. endchoice config ETRAX_SER0_DTR_BIT @@ -141,52 +157,28 @@ config ETRAX_SERIAL_PORT1 Enables the ETRAX FS serial driver for ser1 (ttyS1). choice - prompt "Ser1 DMA in channel " + prompt "Ser1 default port type" depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_NO_DMA_IN - help - What DMA channel to use for ser1. - - -config ETRAX_SERIAL_PORT1_NO_DMA_IN - bool "Ser1 uses no DMA for input" + default ETRAX_SERIAL_PORT1_TYPE_232 help - Do not use DMA for ser1 input. + Type of serial port. -config ETRAX_SERIAL_PORT1_DMA5_IN - bool "Ser1 uses DMA5 for input" - depends on ETRAX_SERIAL_PORT1 +config ETRAX_SERIAL_PORT1_TYPE_232 + bool "Ser1 is a RS-232 port" help - Enables the DMA5 input channel for ser1 (ttyS1). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want this on, unless you use the DMA channel for - something else. - -endchoice + Configure serial port 1 to be a RS-232 port. -choice - prompt "Ser1 DMA out channel " - depends on ETRAX_SERIAL_PORT1 - default ETRAX_SERIAL_PORT1_NO_DMA_OUT - help - What DMA channel to use for ser1. - -config ETRAX_SERIAL_PORT1_NO_DMA_OUT - bool "Ser1 uses no DMA for output" +config ETRAX_SERIAL_PORT1_TYPE_485HD + bool "Ser1 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser1 output. + Configure serial port 1 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT1_DMA4_OUT - bool "Ser1 uses DMA4 for output" - depends on ETRAX_SERIAL_PORT1 +config ETRAX_SERIAL_PORT1_TYPE_485FD + bool "Ser1 is a full duplex RS-485 port" + depends on ETRAX_RS485 help - Enables the DMA4 output channel for ser1 (ttyS1). - If you do not enable DMA, an interrupt for each character will be - used when transmitting data. - Normally you want this on, unless you use the DMA channel for - something else. - + Configure serial port 1 to be a full duplex (four wires) RS-485 port. endchoice config ETRAX_SER1_DTR_BIT @@ -212,52 +204,31 @@ config ETRAX_SERIAL_PORT2 Enables the ETRAX FS serial driver for ser2 (ttyS2). choice - prompt "Ser2 DMA in channel " + prompt "Ser2 default port type" depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_NO_DMA_IN + default ETRAX_SERIAL_PORT2_TYPE_232 help - What DMA channel to use for ser2. + What DMA channel to use for ser2 - -config ETRAX_SERIAL_PORT2_NO_DMA_IN - bool "Ser2 uses no DMA for input" +config ETRAX_SERIAL_PORT2_TYPE_232 + bool "Ser2 is a RS-232 port" help - Do not use DMA for ser2 input. + Configure serial port 2 to be a RS-232 port. -config ETRAX_SERIAL_PORT2_DMA3_IN - bool "Ser2 uses DMA3 for input" - depends on ETRAX_SERIAL_PORT2 - help - Enables the DMA3 input channel for ser2 (ttyS2). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - -endchoice - -choice - prompt "Ser2 DMA out channel" - depends on ETRAX_SERIAL_PORT2 - default ETRAX_SERIAL_PORT2_NO_DMA_OUT - -config ETRAX_SERIAL_PORT2_NO_DMA_OUT - bool "Ser2 uses no DMA for output" +config ETRAX_SERIAL_PORT2_TYPE_485HD + bool "Ser2 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser2 output. + Configure serial port 2 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT2_DMA2_OUT - bool "Ser2 uses DMA2 for output" - depends on ETRAX_SERIAL_PORT2 +config ETRAX_SERIAL_PORT2_TYPE_485FD + bool "Ser2 is a full duplex RS-485 port" + depends on ETRAX_RS485 help - Enables the DMA2 output channel for ser2 (ttyS2). - If you do not enable DMA, an interrupt for each character will be - used when transmitting data. - Normally you want to use DMA, unless you use the DMA channel for - something else. - + Configure serial port 2 to be a full duplex (four wires) RS-485 port. endchoice + config ETRAX_SER2_DTR_BIT string "Ser 2 DTR bit (empty = not used)" depends on ETRAX_SERIAL_PORT2 @@ -281,71 +252,121 @@ config ETRAX_SERIAL_PORT3 Enables the ETRAX FS serial driver for ser3 (ttyS3). choice - prompt "Ser3 DMA in channel " + prompt "Ser3 default port type" depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_NO_DMA_IN + default ETRAX_SERIAL_PORT3_TYPE_232 help What DMA channel to use for ser3. +config ETRAX_SERIAL_PORT3_TYPE_232 + bool "Ser3 is a RS-232 port" + help + Configure serial port 3 to be a RS-232 port. -config ETRAX_SERIAL_PORT3_NO_DMA_IN - bool "Ser3 uses no DMA for input" +config ETRAX_SERIAL_PORT3_TYPE_485HD + bool "Ser3 is a half duplex RS-485 port" + depends on ETRAX_RS485 help - Do not use DMA for ser3 input. + Configure serial port 3 to be a half duplex (two wires) RS-485 port. -config ETRAX_SERIAL_PORT3_DMA9_IN - bool "Ser3 uses DMA9 for input" +config ETRAX_SERIAL_PORT3_TYPE_485FD + bool "Ser3 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 3 to be a full duplex (four wires) RS-485 port. +endchoice + +config ETRAX_SER3_DTR_BIT + string "Ser 3 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_RI_BIT + string "Ser 3 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_DSR_BIT + string "Ser 3 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SER3_CD_BIT + string "Ser 3 CD bit (empty = not used)" depends on ETRAX_SERIAL_PORT3 + +config ETRAX_SERIAL_PORT4 + bool "Serial port 4 enabled" + depends on ETRAXFS_SERIAL && CRIS_MACH_ARTPEC3 help - Enables the DMA9 input channel for ser3 (ttyS3). - If you do not enable DMA, an interrupt for each character will be - used when receiving data. - Normally you want to use DMA, unless you use the DMA channel for - something else. + Enables the ETRAX FS serial driver for ser4 (ttyS4). + +choice + prompt "Ser4 default port type" + depends on ETRAX_SERIAL_PORT4 + default ETRAX_SERIAL_PORT4_TYPE_232 + help + What DMA channel to use for ser4. +config ETRAX_SERIAL_PORT4_TYPE_232 + bool "Ser4 is a RS-232 port" + help + Configure serial port 4 to be a RS-232 port. + +config ETRAX_SERIAL_PORT4_TYPE_485HD + bool "Ser4 is a half duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 4 to be a half duplex (two wires) RS-485 port. + +config ETRAX_SERIAL_PORT4_TYPE_485FD + bool "Ser4 is a full duplex RS-485 port" + depends on ETRAX_RS485 + help + Configure serial port 4 to be a full duplex (four wires) RS-485 port. endchoice choice - prompt "Ser3 DMA out channel" - depends on ETRAX_SERIAL_PORT3 - default ETRAX_SERIAL_PORT3_NO_DMA_OUT + prompt "Ser4 DMA in channel " + depends on ETRAX_SERIAL_PORT4 + default ETRAX_SERIAL_PORT4_NO_DMA_IN + help + What DMA channel to use for ser4. + -config ETRAX_SERIAL_PORT3_NO_DMA_OUT - bool "Ser3 uses no DMA for output" +config ETRAX_SERIAL_PORT4_NO_DMA_IN + bool "Ser4 uses no DMA for input" help - Do not use DMA for ser3 output. + Do not use DMA for ser4 input. -config ETRAX_SERIAL_PORT3_DMA8_OUT - bool "Ser3 uses DMA8 for output" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SERIAL_PORT4_DMA9_IN + bool "Ser4 uses DMA9 for input" + depends on ETRAX_SERIAL_PORT4 help - Enables the DMA8 output channel for ser3 (ttyS3). + Enables the DMA9 input channel for ser4 (ttyS4). If you do not enable DMA, an interrupt for each character will be - used when transmitting data. + used when receiveing data. Normally you want to use DMA, unless you use the DMA channel for something else. endchoice -config ETRAX_SER3_DTR_BIT - string "Ser 3 DTR bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_DTR_BIT + string "Ser 4 DTR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 -config ETRAX_SER3_RI_BIT - string "Ser 3 RI bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_RI_BIT + string "Ser 4 RI bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 -config ETRAX_SER3_DSR_BIT - string "Ser 3 DSR bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 +config ETRAX_SER4_DSR_BIT + string "Ser 4 DSR bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 config ETRAX_SER3_CD_BIT - string "Ser 3 CD bit (empty = not used)" - depends on ETRAX_SERIAL_PORT3 + string "Ser 4 CD bit (empty = not used)" + depends on ETRAX_SERIAL_PORT4 config ETRAX_RS485 bool "RS-485 support" - depends on ETRAX_SERIAL + depends on ETRAXFS_SERIAL help Enables support for RS-485 serial communication. For a primer on RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>. @@ -356,22 +377,6 @@ config ETRAX_RS485_DISABLE_RECEIVER help It is necessary to disable the serial receiver to avoid serial loopback. Not all products are able to do this in software only. - Axis 2400/2401 must disable receiver. - -config ETRAX_AXISFLASHMAP - bool "Axis flash-map support" - depends on ETRAX_ARCH_V32 - select MTD - select MTD_CFI - select MTD_CFI_AMDSTD - select MTD_CHAR - select MTD_BLOCK - select MTD_PARTITIONS - select MTD_CONCAT - select MTD_COMPLEX_MAPPINGS - help - This option enables MTD mapping of flash devices. Needed to use - flash memories. If unsure, say Y. config ETRAX_SYNCHRONOUS_SERIAL bool "Synchronous serial-port support" @@ -394,7 +399,7 @@ config ETRAX_SYNCHRONOUS_SERIAL0_DMA config ETRAX_SYNCHRONOUS_SERIAL_PORT1 bool "Synchronous serial port 1 enabled" - depends on ETRAX_SYNCHRONOUS_SERIAL + depends on ETRAX_SYNCHRONOUS_SERIAL && ETRAXFS help Enabled synchronous serial port 1. @@ -405,6 +410,31 @@ config ETRAX_SYNCHRONOUS_SERIAL1_DMA A synchronous serial port can run in manual or DMA mode. Selecting this option will make it run in DMA mode. +config ETRAX_AXISFLASHMAP + bool "Axis flash-map support" + depends on ETRAX_ARCH_V32 + select MTD + select MTD_CFI + select MTD_CFI_AMDSTD + select MTD_JEDECPROBE + select MTD_CHAR + select MTD_BLOCK + select MTD_PARTITIONS + select MTD_CONCAT + select MTD_COMPLEX_MAPPINGS + help + This option enables MTD mapping of flash devices. Needed to use + flash memories. If unsure, say Y. + +config ETRAX_AXISFLASHMAP_MTD0WHOLE + bool "MTD0 is whole boot flash device" + depends on ETRAX_AXISFLASHMAP + default N + help + When this option is not set, mtd0 refers to the first partition + on the boot flash device. When set, mtd0 refers to the whole + device, with mtd1 referring to the first partition etc. + config ETRAX_PTABLE_SECTOR int "Byte-offset of partition table sector" depends on ETRAX_AXISFLASHMAP @@ -425,42 +455,32 @@ config ETRAX_NANDFLASH This option enables MTD mapping of NAND flash devices. Needed to use NAND flash memories. If unsure, say Y. +config ETRAX_NANDBOOT + bool "Boot from NAND flash" + depends on ETRAX_NANDFLASH + help + This options enables booting from NAND flash devices. + Say Y if your boot code, kernel and root file system is in + NAND flash. Say N if they are in NOR flash. + config ETRAX_I2C bool "I2C driver" depends on ETRAX_ARCH_V32 help - This option enabled the I2C driver used by e.g. the RTC driver. + This option enables the I2C driver used by e.g. the RTC driver. -config ETRAX_I2C_DATA_PORT +config ETRAX_V32_I2C_DATA_PORT string "I2C data pin" depends on ETRAX_I2C help The pin to use for I2C data. -config ETRAX_I2C_CLK_PORT +config ETRAX_V32_I2C_CLK_PORT string "I2C clock pin" depends on ETRAX_I2C help The pin to use for I2C clock. -config ETRAX_RTC - bool "Real Time Clock support" - depends on ETRAX_ARCH_V32 - help - Enabled RTC support. - -choice - prompt "RTC chip" - depends on ETRAX_RTC - default ETRAX_PCF8563 - -config ETRAX_PCF8563 - bool "PCF8563" - help - Philips PCF8563 RTC - -endchoice - config ETRAX_GPIO bool "GPIO support" depends on ETRAX_ARCH_V32 @@ -476,33 +496,36 @@ config ETRAX_GPIO Remember that you need to setup the port directions appropriately in the General configuration. -config ETRAX_PA_BUTTON_BITMASK - hex "PA-buttons bitmask" +config ETRAX_VIRTUAL_GPIO + bool "Virtual GPIO support" depends on ETRAX_GPIO - default "0x02" help - This is a bitmask (8 bits) with information about what bits on PA - that are used for buttons. - Most products has a so called TEST button on PA1, if that is true - use 0x02 here. - Use 00 if there are no buttons on PA. - If the bitmask is <> 00 a button driver will be included in the gpio - driver. ETRAX general I/O support must be enabled. + Enables the virtual Etrax general port device (major 120, minor 6). + It uses an I/O expander for the I2C-bus. + +config ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN + int "Virtual GPIO interrupt pin on PA pin" + range 0 7 + depends on ETRAX_VIRTUAL_GPIO + help + The pin to use on PA for virtual gpio interrupt. config ETRAX_PA_CHANGEABLE_DIR hex "PA user changeable dir mask" depends on ETRAX_GPIO - default "0x00" + default "0x00" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (8 bits) with information of what bits in PA that a user can change direction on using ioctl's. Bit set = changeable. - You probably want 0x00 here, but it depends on your hardware. + You probably want 0 here, but it depends on your hardware. config ETRAX_PA_CHANGEABLE_BITS hex "PA user changeable bits mask" depends on ETRAX_GPIO - default "0x00" + default "0x00" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (8 bits) with information of what bits in PA that a user can change the value on using ioctl's. @@ -511,17 +534,19 @@ config ETRAX_PA_CHANGEABLE_BITS config ETRAX_PB_CHANGEABLE_DIR hex "PB user changeable dir mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (18 bits) with information of what bits in PB that a user can change direction on using ioctl's. Bit set = changeable. - You probably want 0x00000 here, but it depends on your hardware. + You probably want 0 here, but it depends on your hardware. config ETRAX_PB_CHANGEABLE_BITS hex "PB user changeable bits mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (18 bits) with information of what bits in PB that a user can change the value on using ioctl's. @@ -530,17 +555,19 @@ config ETRAX_PB_CHANGEABLE_BITS config ETRAX_PC_CHANGEABLE_DIR hex "PC user changeable dir mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if !ETRAXFS help This is a bitmask (18 bits) with information of what bits in PC that a user can change direction on using ioctl's. Bit set = changeable. - You probably want 0x00000 here, but it depends on your hardware. + You probably want 0 here, but it depends on your hardware. config ETRAX_PC_CHANGEABLE_BITS hex "PC user changeable bits mask" depends on ETRAX_GPIO - default "0x00000" + default "0x00000" if ETRAXFS + default "0x00000000" if ETRAXFS help This is a bitmask (18 bits) with information of what bits in PC that a user can change the value on using ioctl's. @@ -548,7 +575,7 @@ config ETRAX_PC_CHANGEABLE_BITS config ETRAX_PD_CHANGEABLE_DIR hex "PD user changeable dir mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PD @@ -558,7 +585,7 @@ config ETRAX_PD_CHANGEABLE_DIR config ETRAX_PD_CHANGEABLE_BITS hex "PD user changeable bits mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PD @@ -567,7 +594,7 @@ config ETRAX_PD_CHANGEABLE_BITS config ETRAX_PE_CHANGEABLE_DIR hex "PE user changeable dir mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PE @@ -577,20 +604,36 @@ config ETRAX_PE_CHANGEABLE_DIR config ETRAX_PE_CHANGEABLE_BITS hex "PE user changeable bits mask" - depends on ETRAX_GPIO + depends on ETRAX_GPIO && ETRAXFS default "0x00000" help This is a bitmask (18 bits) with information of what bits in PE that a user can change the value on using ioctl's. Bit set = changeable. +config ETRAX_PV_CHANGEABLE_DIR + hex "PV user changeable dir mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change direction on using ioctl's. + Bit set = changeable. + You probably want 0x0000 here, but it depends on your hardware. + +config ETRAX_PV_CHANGEABLE_BITS + hex "PV user changeable bits mask" + depends on ETRAX_VIRTUAL_GPIO + default "0x0000" + help + This is a bitmask (16 bits) with information of what bits in PV + that a user can change the value on using ioctl's. + Bit set = changeable. + config ETRAX_CARDBUS bool "Cardbus support" depends on ETRAX_ARCH_V32 - select PCCARD - select CARDBUS select HOTPLUG - select PCCARD_NONSTATIC help Enabled the ETRAX Cardbus driver. @@ -613,4 +656,202 @@ config ETRAX_STREAMCOPROC This option enables a driver for the stream co-processor for cryptographic operations. +source drivers/mmc/Kconfig + +config ETRAX_MMC_IOP + tristate "MMC/SD host driver using IO-processor" + depends on ETRAX_ARCH_V32 && MMC + help + This option enables the SD/MMC host controller interface. + The host controller is implemented using the built in + IO-Processor. Only the SPU is used in this implementation. + +config ETRAX_SPI_MMC +# Make this one of several "choices" (possible simultaneously but +# suggested uniquely) when an IOP driver emerges for "real" MMC/SD +# protocol support. + tristate + depends on !ETRAX_MMC_IOP + default MMC + select SPI + select MMC_SPI + select ETRAX_SPI_MMC_BOARD + +# For the parts that can't be a module (due to restrictions in +# framework elsewhere). +config ETRAX_SPI_MMC_BOARD + boolean + default n + +# While the board info is MMC_SPI only, the drivers are written to be +# independent of MMC_SPI, so we'll keep SPI non-dependent on the +# MMC_SPI config choices (well, except for a single depends-on-line +# for the board-info file until a separate non-MMC SPI board file +# emerges). +# FIXME: When that happens, we'll need to be able to ask for and +# configure non-MMC SPI ports together with MMC_SPI ports (if multiple +# SPI ports are enabled). + +config SPI_ETRAX_SSER + tristate + depends on SPI_MASTER && ETRAX_ARCH_V32 && EXPERIMENTAL + select SPI_BITBANG + help + This enables using an synchronous serial (sser) port as a + SPI master controller on Axis ETRAX FS and later. The + driver can be configured to use any sser port. + +config SPI_ETRAX_GPIO + tristate + depends on SPI_MASTER && ETRAX_ARCH_V32 && EXPERIMENTAL + select SPI_BITBANG + help + This enables using GPIO pins port as a SPI master controller + on Axis ETRAX FS and later. The driver can be configured to + use any GPIO pins. + +config ETRAX_SPI_SSER0 + tristate "SPI using synchronous serial port 0 (sser0)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m + default y if MMC_SPI=y + default y if MMC_SPI=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 0, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER0_DMA + bool "DMA for SPI on sser0 enabled" + depends on ETRAX_SPI_SSER0 + depends on !ETRAX_SERIAL_PORT1_DMA4_OUT && !ETRAX_SERIAL_PORT1_DMA5_IN + default y + help + Say Y if using DMA (dma4/dma5) for SPI on synchronous serial port 0. + +config ETRAX_SPI_MMC_CD_SSER0_PIN + string "MMC/SD card detect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER0_PIN + string "MMC/SD card write-protect pin for SPI on sser0" + depends on ETRAX_SPI_SSER0 && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_SSER1 + tristate "SPI using synchronous serial port 1 (sser1)" + depends on ETRAX_SPI_MMC + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n + select SPI_ETRAX_SSER + help + Say Y for an MMC/SD socket connected to synchronous serial port 1, + or for devices using the SPI protocol on that port. Say m if you + want to build it as a module, which will be named spi_crisv32_sser. + (You need to select MMC separately.) + +config ETRAX_SPI_SSER1_DMA + bool "DMA for SPI on sser1 enabled" + depends on ETRAX_SPI_SSER1 && !ETRAX_ETHERNET_IFACE1 + depends on !ETRAX_SERIAL_PORT0_DMA6_OUT && !ETRAX_SERIAL_PORT0_DMA7_IN + default y + help + Say Y if using DMA (dma6/dma7) for SPI on synchronous serial port 1. + +config ETRAX_SPI_MMC_CD_SSER1_PIN + string "MMC/SD card detect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd12" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_SSER1_PIN + string "MMC/SD card write-protect pin for SPI on sser1" + depends on ETRAX_SPI_SSER1 && MMC_SPI + default "pd9" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +config ETRAX_SPI_GPIO + tristate "Bitbanged SPI using gpio pins" + depends on ETRAX_SPI_MMC + select SPI_ETRAX_GPIO + default m if MMC_SPI=m && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=y && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + default y if MMC_SPI=n && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n + help + Say Y for an MMC/SD socket connected to general I/O pins (but not + a complete synchronous serial ports), or for devices using the SPI + protocol on general I/O pins. Slow and slows down the system. + Say m to build it as a module, which will be called spi_crisv32_gpio. + (You need to select MMC separately.) + +# The default match that of sser0, only because that's how it was tested. +config ETRAX_SPI_CS_PIN + string "SPI chip select pin" + depends on ETRAX_SPI_GPIO + default "pc3" + help + The pin to use for SPI chip select. + +config ETRAX_SPI_CLK_PIN + string "SPI clock pin" + depends on ETRAX_SPI_GPIO + default "pc1" + help + The pin to use for the SPI clock. + +config ETRAX_SPI_DATAIN_PIN + string "SPI MISO (data in) pin" + depends on ETRAX_SPI_GPIO + default "pc16" + help + The pin to use for SPI data in from the device. + +config ETRAX_SPI_DATAOUT_PIN + string "SPI MOSI (data out) pin" + depends on ETRAX_SPI_GPIO + default "pc0" + help + The pin to use for SPI data out to the device. + +config ETRAX_SPI_MMC_CD_GPIO_PIN + string "MMC/SD card detect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd11" + help + The pin to use for SD/MMC card detect. This pin should be pulled up + and grounded when a card is present. If defined as " " (space), no + pin is selected. A card must then always be inserted for proper + action. + +config ETRAX_SPI_MMC_WP_GPIO_PIN + string "MMC/SD card write-protect pin for SPI using gpio (space for none)" + depends on ETRAX_SPI_GPIO && MMC_SPI + default "pd10" + help + The pin to use for the SD/MMC write-protect signal for a memory + card. If defined as " " (space), the card is considered writable. + +# Avoid choices causing non-working configs by conditionalizing the inclusion. +if ETRAX_SPI_MMC +source drivers/spi/Kconfig +endif + endif diff --git a/arch/cris/arch-v32/drivers/Makefile b/arch/cris/arch-v32/drivers/Makefile index a359cd20ae7..e8c02437eda 100644 --- a/arch/cris/arch-v32/drivers/Makefile +++ b/arch/cris/arch-v32/drivers/Makefile @@ -4,10 +4,11 @@ obj-$(CONFIG_ETRAX_STREAMCOPROC) += cryptocop.o obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o -obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o -obj-$(CONFIG_ETRAX_GPIO) += gpio.o +obj-$(CONFIG_ETRAXFS) += mach-fs/ +obj-$(CONFIG_CRIS_MACH_ARTPEC3) += mach-a3/ obj-$(CONFIG_ETRAX_IOP_FW_LOAD) += iop_fw_load.o obj-$(CONFIG_ETRAX_PCF8563) += pcf8563.o obj-$(CONFIG_ETRAX_I2C) += i2c.o obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL) += sync_serial.o obj-$(CONFIG_PCI) += pci/ +obj-$(CONFIG_ETRAX_SPI_MMC_BOARD) += board_mmcspi.o diff --git a/arch/cris/arch-v32/drivers/axisflashmap.c b/arch/cris/arch-v32/drivers/axisflashmap.c index c5ff95e1826..51e1e85df96 100644 --- a/arch/cris/arch-v32/drivers/axisflashmap.c +++ b/arch/cris/arch-v32/drivers/axisflashmap.c @@ -1,7 +1,7 @@ /* * Physical mapping layer for MTD using the Axis partitiontable format * - * Copyright (c) 2001, 2002, 2003 Axis Communications AB + * Copyright (c) 2001-2007 Axis Communications AB * * This file is under the GPL. * @@ -10,9 +10,6 @@ * tells us what other partitions to define. If there isn't, we use a default * partition split defined below. * - * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5 - * with minor changes. - * */ #include <linux/module.h> @@ -27,7 +24,8 @@ #include <linux/mtd/mtdram.h> #include <linux/mtd/partitions.h> -#include <asm/arch/hwregs/config_defs.h> +#include <linux/cramfs_fs.h> + #include <asm/axisflashmap.h> #include <asm/mmu.h> @@ -37,16 +35,24 @@ #define FLASH_UNCACHED_ADDR KSEG_E #define FLASH_CACHED_ADDR KSEG_F +#define PAGESIZE (512) + #if CONFIG_ETRAX_FLASH_BUSWIDTH==1 #define flash_data __u8 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==2 #define flash_data __u16 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==4 -#define flash_data __u16 +#define flash_data __u32 #endif /* From head.S */ -extern unsigned long romfs_start, romfs_length, romfs_in_flash; +extern unsigned long romfs_in_flash; /* 1 when romfs_start, _length in flash */ +extern unsigned long romfs_start, romfs_length; +extern unsigned long nand_boot; /* 1 when booted from nand flash */ + +struct partition_name { + char name[6]; +}; /* The master mtd for the entire flash. */ struct mtd_info* axisflash_mtd = NULL; @@ -112,32 +118,20 @@ static struct map_info map_cse1 = { .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE }; -/* If no partition-table was found, we use this default-set. */ -#define MAX_PARTITIONS 7 -#define NUM_DEFAULT_PARTITIONS 3 +#define MAX_PARTITIONS 7 +#ifdef CONFIG_ETRAX_NANDBOOT +#define NUM_DEFAULT_PARTITIONS 4 +#define DEFAULT_ROOTFS_PARTITION_NO 2 +#define DEFAULT_MEDIA_SIZE 0x2000000 /* 32 megs */ +#else +#define NUM_DEFAULT_PARTITIONS 3 +#define DEFAULT_ROOTFS_PARTITION_NO (-1) +#define DEFAULT_MEDIA_SIZE 0x800000 /* 8 megs */ +#endif -/* - * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the - * size of one flash block and "filesystem"-partition needs 5 blocks to be able - * to use JFFS. - */ -static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { - { - .name = "boot firmware", - .size = CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0 - }, - { - .name = "kernel", - .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR), - .offset = CONFIG_ETRAX_PTABLE_SECTOR - }, - { - .name = "filesystem", - .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR, - .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR) - } -}; +#if (MAX_PARTITIONS < NUM_DEFAULT_PARTITIONS) +#error MAX_PARTITIONS must be >= than NUM_DEFAULT_PARTITIONS +#endif /* Initialize the ones normally used. */ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { @@ -178,6 +172,56 @@ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { }, }; + +/* If no partition-table was found, we use this default-set. + * Default flash size is 8MB (NOR). CONFIG_ETRAX_PTABLE_SECTOR is most + * likely the size of one flash block and "filesystem"-partition needs + * to be >=5 blocks to be able to use JFFS. + */ +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + .name = "boot firmware", + .size = CONFIG_ETRAX_PTABLE_SECTOR, + .offset = 0 + }, + { + .name = "kernel", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = CONFIG_ETRAX_PTABLE_SECTOR + }, +#define FILESYSTEM_SECTOR (11 * CONFIG_ETRAX_PTABLE_SECTOR) +#ifdef CONFIG_ETRAX_NANDBOOT + { + .name = "rootfs", + .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR, + .offset = FILESYSTEM_SECTOR + }, +#undef FILESYSTEM_SECTOR +#define FILESYSTEM_SECTOR (21 * CONFIG_ETRAX_PTABLE_SECTOR) +#endif + { + .name = "rwfs", + .size = DEFAULT_MEDIA_SIZE - FILESYSTEM_SECTOR, + .offset = FILESYSTEM_SECTOR + } +}; + +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE +/* Main flash device */ +static struct mtd_partition main_partition = { + .name = "main", + .size = 0, + .offset = 0 +}; +#endif + +/* Auxilliary partition if we find another flash */ +static struct mtd_partition aux_partition = { + .name = "aux", + .size = 0, + .offset = 0 +}; + /* * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash * chips in that order (because the amd_flash-driver is faster). @@ -191,7 +235,7 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) map_cs->name, map_cs->size, map_cs->map_priv_1); #ifdef CONFIG_MTD_CFI - mtd_cs = do_map_probe("cfi_probe", map_cs); + mtd_cs = do_map_probe("cfi_probe", map_cs); #endif #ifdef CONFIG_MTD_JEDECPROBE if (!mtd_cs) @@ -204,7 +248,7 @@ static struct mtd_info *probe_cs(struct map_info *map_cs) /* * Probe each chip select individually for flash chips. If there are chips on * both cse0 and cse1, the mtd_info structs will be concatenated to one struct - * so that MTD partitions can cross chip boundaries. + * so that MTD partitions can cross chip boundries. * * The only known restriction to how you can mount your chips is that each * chip select must hold similar flash chips. But you need external hardware @@ -216,9 +260,8 @@ static struct mtd_info *flash_probe(void) { struct mtd_info *mtd_cse0; struct mtd_info *mtd_cse1; - struct mtd_info *mtd_nand = NULL; struct mtd_info *mtd_total; - struct mtd_info *mtds[3]; + struct mtd_info *mtds[2]; int count = 0; if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL) @@ -226,12 +269,7 @@ static struct mtd_info *flash_probe(void) if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL) mtds[count++] = mtd_cse1; -#ifdef CONFIG_ETRAX_NANDFLASH - if ((mtd_nand = crisv32_nand_flash_probe()) != NULL) - mtds[count++] = mtd_nand; -#endif - - if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) { + if (!mtd_cse0 && !mtd_cse1) { /* No chip found. */ return NULL; } @@ -245,9 +283,7 @@ static struct mtd_info *flash_probe(void) * So we use the MTD concatenation layer instead of further * complicating the probing procedure. */ - mtd_total = mtd_concat_create(mtds, - count, - "cse0+cse1+nand"); + mtd_total = mtd_concat_create(mtds, count, "cse0+cse1"); #else printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel " "(mis)configuration!\n", map_cse0.name, map_cse1.name); @@ -255,61 +291,162 @@ static struct mtd_info *flash_probe(void) #endif if (!mtd_total) { printk(KERN_ERR "%s and %s: Concatenation failed!\n", - map_cse0.name, map_cse1.name); + map_cse0.name, map_cse1.name); /* The best we can do now is to only use what we found - * at cse0. - */ + * at cse0. */ mtd_total = mtd_cse0; map_destroy(mtd_cse1); } - } else { - mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand; - - } + } else + mtd_total = mtd_cse0 ? mtd_cse0 : mtd_cse1; return mtd_total; } -extern unsigned long crisv32_nand_boot; -extern unsigned long crisv32_nand_cramfs_offset; - /* * Probe the flash chip(s) and, if it succeeds, read the partition-table * and register the partitions with MTD. */ static int __init init_axis_flash(void) { - struct mtd_info *mymtd; + struct mtd_info *main_mtd; + struct mtd_info *aux_mtd = NULL; int err = 0; int pidx = 0; struct partitiontable_head *ptable_head = NULL; struct partitiontable_entry *ptable; - int use_default_ptable = 1; /* Until proven otherwise. */ - const char *pmsg = KERN_INFO " /dev/flash%d at 0x%08x, size 0x%08x\n"; - static char page[512]; + int ptable_ok = 0; + static char page[PAGESIZE]; size_t len; + int ram_rootfs_partition = -1; /* -1 => no RAM rootfs partition */ + int part; + + /* We need a root fs. If it resides in RAM, we need to use an + * MTDRAM device, so it must be enabled in the kernel config, + * but its size must be configured as 0 so as not to conflict + * with our usage. + */ +#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) + if (!romfs_in_flash && !nand_boot) { + printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " + "device; configure CONFIG_MTD_MTDRAM with size = 0!\n"); + panic("This kernel cannot boot from RAM!\n"); + } +#endif + +#ifndef CONFIG_ETRAX_VCS_SIM + main_mtd = flash_probe(); + if (main_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NOR flash memory.\n", + main_mtd->name, main_mtd->size); + +#ifdef CONFIG_ETRAX_NANDFLASH + aux_mtd = crisv32_nand_flash_probe(); + if (aux_mtd) + printk(KERN_INFO "%s: 0x%08x bytes of NAND flash memory.\n", + aux_mtd->name, aux_mtd->size); + +#ifdef CONFIG_ETRAX_NANDBOOT + { + struct mtd_info *tmp_mtd; -#ifndef CONFIG_ETRAXFS_SIM - mymtd = flash_probe(); - mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page); - ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET); + printk(KERN_INFO "axisflashmap: Set to boot from NAND flash, " + "making NAND flash primary device.\n"); + tmp_mtd = main_mtd; + main_mtd = aux_mtd; + aux_mtd = tmp_mtd; + } +#endif /* CONFIG_ETRAX_NANDBOOT */ +#endif /* CONFIG_ETRAX_NANDFLASH */ - if (!mymtd) { + if (!main_mtd && !aux_mtd) { /* There's no reason to use this module if no flash chip can * be identified. Make sure that's understood. */ printk(KERN_INFO "axisflashmap: Found no flash chip.\n"); - } else { - printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n", - mymtd->name, mymtd->size); - axisflash_mtd = mymtd; } - if (mymtd) { - mymtd->owner = THIS_MODULE; +#if 0 /* Dump flash memory so we can see what is going on */ + if (main_mtd) { + int sectoraddr, i; + for (sectoraddr = 0; sectoraddr < 2*65536+4096; + sectoraddr += PAGESIZE) { + main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len, + page); + printk(KERN_INFO + "Sector at %d (length %d):\n", + sectoraddr, len); + for (i = 0; i < PAGESIZE; i += 16) { + printk(KERN_INFO + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + page[i] & 255, page[i+1] & 255, + page[i+2] & 255, page[i+3] & 255, + page[i+4] & 255, page[i+5] & 255, + page[i+6] & 255, page[i+7] & 255, + page[i+8] & 255, page[i+9] & 255, + page[i+10] & 255, page[i+11] & 255, + page[i+12] & 255, page[i+13] & 255, + page[i+14] & 255, page[i+15] & 255); + } + } + } +#endif + + if (main_mtd) { + main_mtd->owner = THIS_MODULE; + axisflash_mtd = main_mtd; + + loff_t ptable_sector = CONFIG_ETRAX_PTABLE_SECTOR; + + /* First partition (rescue) is always set to the default. */ + pidx++; +#ifdef CONFIG_ETRAX_NANDBOOT + /* We know where the partition table should be located, + * it will be in first good block after that. + */ + int blockstat; + do { + blockstat = main_mtd->block_isbad(main_mtd, + ptable_sector); + if (blockstat < 0) + ptable_sector = 0; /* read error */ + else if (blockstat) + ptable_sector += main_mtd->erasesize; + } while (blockstat && ptable_sector); +#endif + if (ptable_sector) { + main_mtd->read(main_mtd, ptable_sector, PAGESIZE, + &len, page); + ptable_head = &((struct partitiontable *) page)->head; + } + +#if 0 /* Dump partition table so we can see what is going on */ + printk(KERN_INFO + "axisflashmap: flash read %d bytes at 0x%08x, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + len, CONFIG_ETRAX_PTABLE_SECTOR, + page[0] & 255, page[1] & 255, + page[2] & 255, page[3] & 255, + page[4] & 255, page[5] & 255, + page[6] & 255, page[7] & 255); + printk(KERN_INFO + "axisflashmap: partition table offset %d, data: " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + PARTITION_TABLE_OFFSET, + page[PARTITION_TABLE_OFFSET+0] & 255, + page[PARTITION_TABLE_OFFSET+1] & 255, + page[PARTITION_TABLE_OFFSET+2] & 255, + page[PARTITION_TABLE_OFFSET+3] & 255, + page[PARTITION_TABLE_OFFSET+4] & 255, + page[PARTITION_TABLE_OFFSET+5] & 255, + page[PARTITION_TABLE_OFFSET+6] & 255, + page[PARTITION_TABLE_OFFSET+7] & 255); +#endif } - pidx++; /* First partition is always set to the default. */ if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC) && (ptable_head->size < @@ -322,7 +459,6 @@ static int __init init_axis_flash(void) /* Looks like a start, sane length and end of a * partition table, lets check csum etc. */ - int ptable_ok = 0; struct partitiontable_entry *max_addr = (struct partitiontable_entry *) ((unsigned long)ptable_head + sizeof(*ptable_head) + @@ -346,104 +482,170 @@ static int __init init_axis_flash(void) ptable_ok = (csum == ptable_head->checksum); /* Read the entries and use/show the info. */ - printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n", + printk(KERN_INFO "axisflashmap: " + "Found a%s partition table at 0x%p-0x%p.\n", (ptable_ok ? " valid" : "n invalid"), ptable_head, max_addr); /* We have found a working bootblock. Now read the - * partition table. Scan the table. It ends when - * there is 0xffffffff, that is, empty flash. + * partition table. Scan the table. It ends with 0xffffffff. */ while (ptable_ok - && ptable->offset != 0xffffffff + && ptable->offset != PARTITIONTABLE_END_MARKER && ptable < max_addr - && pidx < MAX_PARTITIONS) { + && pidx < MAX_PARTITIONS - 1) { - axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0); - axis_partitions[pidx].size = ptable->size; - - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); + axis_partitions[pidx].offset = offset + ptable->offset; +#ifdef CONFIG_ETRAX_NANDFLASH + if (main_mtd->type == MTD_NANDFLASH) { + axis_partitions[pidx].size = + (((ptable+1)->offset == + PARTITIONTABLE_END_MARKER) ? + main_mtd->size : + ((ptable+1)->offset + offset)) - + (ptable->offset + offset); + + } else +#endif /* CONFIG_ETRAX_NANDFLASH */ + axis_partitions[pidx].size = ptable->size; +#ifdef CONFIG_ETRAX_NANDBOOT + /* Save partition number of jffs2 ro partition. + * Needed if RAM booting or root file system in RAM. + */ + if (!nand_boot && + ram_rootfs_partition < 0 && /* not already set */ + ptable->type == PARTITION_TYPE_JFFS2 && + (ptable->flags & PARTITION_FLAGS_READONLY_MASK) == + PARTITION_FLAGS_READONLY) + ram_rootfs_partition = pidx; +#endif /* CONFIG_ETRAX_NANDBOOT */ pidx++; ptable++; } - use_default_ptable = !ptable_ok; } - if (romfs_in_flash) { - /* Add an overlapping device for the root partition (romfs). */ + /* Decide whether to use default partition table. */ + /* Only use default table if we actually have a device (main_mtd) */ - axis_partitions[pidx].name = "romfs"; - if (crisv32_nand_boot) { - char* data = kmalloc(1024, GFP_KERNEL); - int len; - int offset = crisv32_nand_cramfs_offset & ~(1024-1); - char* tmp; - - mymtd->read(mymtd, offset, 1024, &len, data); - tmp = &data[crisv32_nand_cramfs_offset % 512]; - axis_partitions[pidx].size = *(unsigned*)(tmp + 4); - axis_partitions[pidx].offset = crisv32_nand_cramfs_offset; - kfree(data); - } else { - axis_partitions[pidx].size = romfs_length; - axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; - } + struct mtd_partition *partition = &axis_partitions[0]; + if (main_mtd && !ptable_ok) { + memcpy(axis_partitions, axis_default_partitions, + sizeof(axis_default_partitions)); + pidx = NUM_DEFAULT_PARTITIONS; + ram_rootfs_partition = DEFAULT_ROOTFS_PARTITION_NO; + } + /* Add artificial partitions for rootfs if necessary */ + if (romfs_in_flash) { + /* rootfs is in directly accessible flash memory = NOR flash. + Add an overlapping device for the rootfs partition. */ + printk(KERN_INFO "axisflashmap: Adding partition for " + "overlapping root file system image\n"); + axis_partitions[pidx].size = romfs_length; + axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR; + axis_partitions[pidx].name = "romfs"; axis_partitions[pidx].mask_flags |= MTD_WRITEABLE; - - printk(KERN_INFO - " Adding readonly flash partition for romfs image:\n"); - printk(pmsg, pidx, axis_partitions[pidx].offset, - axis_partitions[pidx].size); + ram_rootfs_partition = -1; pidx++; - } - - if (mymtd) { - if (use_default_ptable) { - printk(KERN_INFO " Using default partition table.\n"); - err = add_mtd_partitions(mymtd, axis_default_partitions, - NUM_DEFAULT_PARTITIONS); - } else { - err = add_mtd_partitions(mymtd, axis_partitions, pidx); + } else if (romfs_length && !nand_boot) { + /* romfs exists in memory, but not in flash, so must be in RAM. + * Configure an MTDRAM partition. */ + if (ram_rootfs_partition < 0) { + /* None set yet, put it at the end */ + ram_rootfs_partition = pidx; + pidx++; } + printk(KERN_INFO "axisflashmap: Adding partition for " + "root file system image in RAM\n"); + axis_partitions[ram_rootfs_partition].size = romfs_length; + axis_partitions[ram_rootfs_partition].offset = romfs_start; + axis_partitions[ram_rootfs_partition].name = "romfs"; + axis_partitions[ram_rootfs_partition].mask_flags |= + MTD_WRITEABLE; + } - if (err) { - panic("axisflashmap could not add MTD partitions!\n"); - } +#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE + if (main_mtd) { + main_partition.size = main_mtd->size; + err = add_mtd_partitions(main_mtd, &main_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "partition for whole main mtd device!\n"); } -/* CONFIG_EXTRAXFS_SIM */ #endif - if (!romfs_in_flash) { - /* Create an RAM device for the root partition (romfs). */ + /* Now, register all partitions with mtd. + * We do this one at a time so we can slip in an MTDRAM device + * in the proper place if required. */ + + for (part = 0; part < pidx; part++) { + if (part == ram_rootfs_partition) { + /* add MTDRAM partition here */ + struct mtd_info *mtd_ram; + + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) + panic("axisflashmap: Couldn't allocate memory " + "for mtd_info!\n"); + printk(KERN_INFO "axisflashmap: Adding RAM partition " + "for rootfs image.\n"); + err = mtdram_init_device(mtd_ram, + (void *)partition[part].offset, + partition[part].size, + partition[part].name); + if (err) + panic("axisflashmap: Could not initialize " + "MTD RAM device!\n"); + /* JFFS2 likes to have an erasesize. Keep potential + * JFFS2 rootfs happy by providing one. Since image + * was most likely created for main mtd, use that + * erasesize, if available. Otherwise, make a guess. */ + mtd_ram->erasesize = (main_mtd ? main_mtd->erasesize : + CONFIG_ETRAX_PTABLE_SECTOR); + } else { + err = add_mtd_partitions(main_mtd, &partition[part], 1); + if (err) + panic("axisflashmap: Could not add mtd " + "partition %d\n", part); + } + } +#endif /* CONFIG_EXTRAX_VCS_SIM */ + +#ifdef CONFIG_ETRAX_VCS_SIM + /* For simulator, always use a RAM partition. + * The rootfs will be found after the kernel in RAM, + * with romfs_start and romfs_end indicating location and size. + */ + struct mtd_info *mtd_ram; + + mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_ram) { + panic("axisflashmap: Couldn't allocate memory for " + "mtd_info!\n"); + } -#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0) - /* No use trying to boot this kernel from RAM. Panic! */ - printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM " - "device due to kernel (mis)configuration!\n"); - panic("This kernel cannot boot from RAM!\n"); -#else - struct mtd_info *mtd_ram; + printk(KERN_INFO "axisflashmap: Adding RAM partition for romfs, " + "at %u, size %u\n", + (unsigned) romfs_start, (unsigned) romfs_length); - mtd_ram = kmalloc(sizeof(struct mtd_info), - GFP_KERNEL); - if (!mtd_ram) { - panic("axisflashmap couldn't allocate memory for " - "mtd_info!\n"); - } + err = mtdram_init_device(mtd_ram, (void *)romfs_start, + romfs_length, "romfs"); + if (err) { + panic("axisflashmap: Could not initialize MTD RAM " + "device!\n"); + } +#endif /* CONFIG_EXTRAX_VCS_SIM */ - printk(KERN_INFO " Adding RAM partition for romfs image:\n"); - printk(pmsg, pidx, romfs_start, romfs_length); +#ifndef CONFIG_ETRAX_VCS_SIM + if (aux_mtd) { + aux_partition.size = aux_mtd->size; + err = add_mtd_partitions(aux_mtd, &aux_partition, 1); + if (err) + panic("axisflashmap: Could not initialize " + "aux mtd device!\n"); - err = mtdram_init_device(mtd_ram, (void*)romfs_start, - romfs_length, "romfs"); - if (err) { - panic("axisflashmap could not initialize MTD RAM " - "device!\n"); - } -#endif } +#endif /* CONFIG_EXTRAX_VCS_SIM */ return err; } diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c index e8914d40169..9fb58202be9 100644 --- a/arch/cris/arch-v32/drivers/cryptocop.c +++ b/arch/cris/arch-v32/drivers/cryptocop.c @@ -1,8 +1,7 @@ -/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $ - * +/* * Stream co-processor driver for the ETRAX FS * - * Copyright (C) 2003-2005 Axis Communications AB + * Copyright (C) 2003-2007 Axis Communications AB */ #include <linux/init.h> @@ -25,17 +24,29 @@ #include <asm/signal.h> #include <asm/irq.h> -#include <asm/arch/dma.h> -#include <asm/arch/hwregs/dma.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/intr_vect_defs.h> - -#include <asm/arch/hwregs/strcop.h> -#include <asm/arch/hwregs/strcop_defs.h> -#include <asm/arch/cryptocop.h> - - +#include <dma.h> +#include <hwregs/dma.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/intr_vect_defs.h> + +#include <hwregs/strcop.h> +#include <hwregs/strcop_defs.h> +#include <cryptocop.h> + +#ifdef CONFIG_ETRAXFS +#define IN_DMA 9 +#define OUT_DMA 8 +#define IN_DMA_INST regi_dma9 +#define OUT_DMA_INST regi_dma8 +#define DMA_IRQ DMA9_INTR_VECT +#else +#define IN_DMA 3 +#define OUT_DMA 2 +#define IN_DMA_INST regi_dma3 +#define OUT_DMA_INST regi_dma2 +#define DMA_IRQ DMA3_INTR_VECT +#endif #define DESCR_ALLOC_PAD (31) @@ -1886,14 +1897,14 @@ static void cryptocop_do_tasklet(unsigned long unused) } static irqreturn_t -dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) +dma_done_interrupt(int irq, void *dev_id) { struct cryptocop_prio_job *done_job; reg_dma_rw_ack_intr ack_intr = { .data = 1, }; - REG_WR (dma, regi_dma9, rw_ack_intr, ack_intr); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); DEBUG(printk("cryptocop DMA done\n")); @@ -1937,7 +1948,6 @@ dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs) static int init_cryptocop(void) { unsigned long flags; - reg_intr_vect_rw_mask intr_mask; reg_dma_rw_cfg dma_cfg = {.en = 1}; reg_dma_rw_intr_mask intr_mask_in = {.data = regk_dma_yes}; /* Only want descriptor interrupts from the DMA in channel. */ reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 }; @@ -1950,10 +1960,14 @@ static int init_cryptocop(void) .en = 1 }; - if (request_irq(DMA9_INTR_VECT, dma_done_interrupt, 0, "stream co-processor DMA", NULL)) panic("request_irq stream co-processor irq dma9"); + if (request_irq(DMA_IRQ, dma_done_interrupt, 0, + "stream co-processor DMA", NULL)) + panic("request_irq stream co-processor irq dma9"); - (void)crisv32_request_dma(8, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp); - (void)crisv32_request_dma(9, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp); + (void)crisv32_request_dma(OUT_DMA, "strcop", DMA_PANIC_ON_ERROR, + 0, dma_strp); + (void)crisv32_request_dma(IN_DMA, "strcop", DMA_PANIC_ON_ERROR, + 0, dma_strp); local_irq_save(flags); @@ -1963,24 +1977,19 @@ static int init_cryptocop(void) strcop_cfg.en = 1; REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg); - /* Enable DMA9 interrupt */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.dma9 = 1; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - /* Enable DMAs. */ - REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */ - REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */ + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */ /* Set up wordsize = 4 for DMAs. */ - DMA_WR_CMD (regi_dma8, regk_dma_set_w_size4); - DMA_WR_CMD (regi_dma9, regk_dma_set_w_size4); + DMA_WR_CMD(OUT_DMA_INST, regk_dma_set_w_size4); + DMA_WR_CMD(IN_DMA_INST, regk_dma_set_w_size4); /* Enable interrupts. */ - REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in); /* Clear intr ack. */ - REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); local_irq_restore(flags); @@ -1991,7 +2000,6 @@ static int init_cryptocop(void) static void release_cryptocop(void) { unsigned long flags; - reg_intr_vect_rw_mask intr_mask; reg_dma_rw_cfg dma_cfg = {.en = 0}; reg_dma_rw_intr_mask intr_mask_in = {0}; reg_dma_rw_ack_intr ack_intr = {.data = 1,.in_eop = 1 }; @@ -1999,26 +2007,21 @@ static void release_cryptocop(void) local_irq_save(flags); /* Clear intr ack. */ - REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr); - - /* Disable DMA9 interrupt */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.dma9 = 0; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + REG_WR(dma, IN_DMA_INST, rw_ack_intr, ack_intr); /* Disable DMAs. */ - REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */ - REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */ + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_cfg); /* input DMA */ + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_cfg); /* output DMA */ /* Disable interrupts. */ - REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in); + REG_WR(dma, IN_DMA_INST, rw_intr_mask, intr_mask_in); local_irq_restore(flags); - free_irq(DMA9_INTR_VECT, NULL); + free_irq(DMA_IRQ, NULL); - (void)crisv32_free_dma(8); - (void)crisv32_free_dma(9); + (void)crisv32_free_dma(OUT_DMA); + (void)crisv32_free_dma(IN_DMA); } @@ -2076,13 +2079,13 @@ static void cryptocop_job_queue_close(void) reg_dma_rw_cfg dma_out_cfg, dma_in_cfg; /* Stop DMA. */ - dma_out_cfg = REG_RD(dma, regi_dma8, rw_cfg); + dma_out_cfg = REG_RD(dma, OUT_DMA_INST, rw_cfg); dma_out_cfg.en = regk_dma_no; - REG_WR(dma, regi_dma8, rw_cfg, dma_out_cfg); + REG_WR(dma, OUT_DMA_INST, rw_cfg, dma_out_cfg); - dma_in_cfg = REG_RD(dma, regi_dma9, rw_cfg); + dma_in_cfg = REG_RD(dma, IN_DMA_INST, rw_cfg); dma_in_cfg.en = regk_dma_no; - REG_WR(dma, regi_dma9, rw_cfg, dma_in_cfg); + REG_WR(dma, IN_DMA_INST, rw_cfg, dma_in_cfg); /* Disble the cryptocop. */ rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg); @@ -2226,10 +2229,11 @@ static void cryptocop_start_job(void) &pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out))); /* Start input DMA. */ - DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in)); + flush_dma_context(&pj->iop->ctx_in); + DMA_START_CONTEXT(IN_DMA_INST, virt_to_phys(&pj->iop->ctx_in)); /* Start output DMA. */ - DMA_START_CONTEXT(regi_dma8, virt_to_phys(&pj->iop->ctx_out)); + DMA_START_CONTEXT(OUT_DMA_INST, virt_to_phys(&pj->iop->ctx_out)); spin_unlock_irqrestore(&running_job_lock, running_job_flags); DEBUG(printk("cryptocop_start_job: exiting\n")); diff --git a/arch/cris/arch-v32/drivers/i2c.c b/arch/cris/arch-v32/drivers/i2c.c index f1edd2e359b..c2fb7a5c139 100644 --- a/arch/cris/arch-v32/drivers/i2c.c +++ b/arch/cris/arch-v32/drivers/i2c.c @@ -19,10 +19,10 @@ *! *! --------------------------------------------------------------------------- *! -*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN +*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN *! *!***************************************************************************/ -/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */ + /****************** INCLUDE FILES SECTION ***********************************/ #include <linux/module.h> @@ -79,6 +79,8 @@ static const char i2c_name[] = "i2c"; #define i2c_delay(usecs) udelay(usecs) +static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */ + /****************** VARIABLE SECTION ************************************/ static struct crisv32_iopin cris_i2c_clk; @@ -252,6 +254,7 @@ i2c_getack(void) * generate ACK clock pulse */ i2c_clk(I2C_CLOCK_HIGH); +#if 0 /* * Use PORT PB instead of I2C * for input. (I2C not working) @@ -264,6 +267,8 @@ i2c_getack(void) i2c_data(1); i2c_disable(); i2c_dir_in(); +#endif + /* * now wait for ack */ @@ -271,11 +276,11 @@ i2c_getack(void) /* * check for ack */ - if(i2c_getbit()) + if (i2c_getbit()) ack = 0; i2c_delay(CLOCK_HIGH_TIME/2); - if(!ack){ - if(!i2c_getbit()) /* receiver pulled SDA low */ + if (!ack) { + if (!i2c_getbit()) /* receiver pulld SDA low */ ack = 1; i2c_delay(CLOCK_HIGH_TIME/2); } @@ -285,6 +290,7 @@ i2c_getack(void) * before we enable our output. If we keep data high * and enable output, we would generate a stop condition. */ +#if 0 i2c_data(I2C_DATA_LOW); /* @@ -292,6 +298,7 @@ i2c_getack(void) */ i2c_enable(); i2c_dir_out(); +#endif i2c_clk(I2C_CLOCK_LOW); i2c_delay(CLOCK_HIGH_TIME/4); /* @@ -375,6 +382,121 @@ i2c_sendnack(void) /*#--------------------------------------------------------------------------- *# +*# FUNCTION NAME: i2c_write +*# +*# DESCRIPTION : Writes a value to an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_write(unsigned char theSlave, void *data, size_t nbytes) +{ + int error, cntr = 3; + unsigned char bytes_wrote = 0; + unsigned char value; + unsigned long flags; + + spin_lock_irqsave(&i2c_lock, flags); + + do { + error = 0; + + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave & 0xfe)); + /* + * wait for ack + */ + if (!i2c_getack()) + error = 1; + /* + * send data + */ + for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) { + memcpy(&value, data + bytes_wrote, sizeof value); + i2c_outbyte(value); + /* + * now it's time to wait for ack + */ + if (!i2c_getack()) + error |= 4; + } + /* + * end byte stream + */ + i2c_stop(); + + } while (error && cntr--); + + i2c_delay(CLOCK_LOW_TIME); + + spin_unlock_irqrestore(&i2c_lock, flags); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# +*# FUNCTION NAME: i2c_read +*# +*# DESCRIPTION : Reads a value from an I2C device +*# +*#--------------------------------------------------------------------------*/ +int +i2c_read(unsigned char theSlave, void *data, size_t nbytes) +{ + unsigned char b = 0; + unsigned char bytes_read = 0; + int error, cntr = 3; + unsigned long flags; + + spin_lock_irqsave(&i2c_lock, flags); + + do { + error = 0; + memset(data, 0, nbytes); + /* + * generate start condition + */ + i2c_start(); + /* + * send slave address + */ + i2c_outbyte((theSlave | 0x01)); + /* + * wait for ack + */ + if (!i2c_getack()) + error = 1; + /* + * fetch data + */ + for (bytes_read = 0; bytes_read < nbytes; bytes_read++) { + b = i2c_inbyte(); + memcpy(data + bytes_read, &b, sizeof b); + + if (bytes_read < (nbytes - 1)) + i2c_sendack(); + } + /* + * last received byte needs to be nacked + * instead of acked + */ + i2c_sendnack(); + /* + * end sequence + */ + i2c_stop(); + } while (error && cntr--); + + spin_unlock_irqrestore(&i2c_lock, flags); + + return -error; +} + +/*#--------------------------------------------------------------------------- +*# *# FUNCTION NAME: i2c_writereg *# *# DESCRIPTION : Writes a value to an I2C device @@ -387,12 +509,10 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, int error, cntr = 3; unsigned long flags; + spin_lock_irqsave(&i2c_lock, flags); + do { error = 0; - /* - * we don't like to be interrupted - */ - local_irq_save(flags); i2c_start(); /* @@ -427,15 +547,12 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg, * end byte stream */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); - } while(error && cntr--); i2c_delay(CLOCK_LOW_TIME); + spin_unlock_irqrestore(&i2c_lock, flags); + return -error; } @@ -453,13 +570,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) int error, cntr = 3; unsigned long flags; + spin_lock_irqsave(&i2c_lock, flags); + do { error = 0; /* - * we don't like to be interrupted - */ - local_irq_save(flags); - /* * generate start condition */ i2c_start(); @@ -482,7 +597,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * now it's time to wait for ack */ if(!i2c_getack()) - error = 1; + error |= 2; /* * repeat start condition */ @@ -496,7 +611,7 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * wait for ack */ if(!i2c_getack()) - error = 1; + error |= 4; /* * fetch register */ @@ -510,13 +625,11 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg) * end sequence */ i2c_stop(); - /* - * enable interrupt again - */ - local_irq_restore(flags); } while(error && cntr--); + spin_unlock_irqrestore(&i2c_lock, flags); + return b; } @@ -540,7 +653,7 @@ i2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) { - return -EINVAL; + return -ENOTTY; } switch (_IOC_NR(cmd)) { @@ -580,31 +693,52 @@ static const struct file_operations i2c_fops = { .release = i2c_release, }; -int __init -i2c_init(void) +static int __init i2c_init(void) { - int res; + static int res; + static int first = 1; - /* Setup and enable the Port B I2C interface */ + if (!first) + return res; - crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT); - crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT); + first = 0; + + /* Setup and enable the DATA and CLK pins */ + + res = crisv32_io_get_name(&cris_i2c_data, + CONFIG_ETRAX_V32_I2C_DATA_PORT); + if (res < 0) + return res; + + res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_V32_I2C_CLK_PORT); + crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out); + + return res; +} + + +static int __init i2c_register(void) +{ + int res; + + res = i2c_init(); + if (res < 0) + return res; /* register char device */ res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops); - if(res < 0) { + if (res < 0) { printk(KERN_ERR "i2c: couldn't get a major number.\n"); return res; } - printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n"); + printk(KERN_INFO + "I2C driver v2.2, (c) 1999-2007 Axis Communications AB\n"); return 0; } - /* this makes sure that i2c_init is called during boot */ - -module_init(i2c_init); +module_init(i2c_register); /****************** END OF FILE i2c.c ********************************/ diff --git a/arch/cris/arch-v32/drivers/i2c.h b/arch/cris/arch-v32/drivers/i2c.h index bfe1a13f9f3..c073cf4ba01 100644 --- a/arch/cris/arch-v32/drivers/i2c.h +++ b/arch/cris/arch-v32/drivers/i2c.h @@ -3,6 +3,8 @@ /* High level I2C actions */ int __init i2c_init(void); +int i2c_write(unsigned char theSlave, void *data, size_t nbytes); +int i2c_read(unsigned char theSlave, void *data, size_t nbytes); int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue); unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg); diff --git a/arch/cris/arch-v32/drivers/iop_fw_load.c b/arch/cris/arch-v32/drivers/iop_fw_load.c index f4bdc1dfa32..3b3857ec1f1 100644 --- a/arch/cris/arch-v32/drivers/iop_fw_load.c +++ b/arch/cris/arch-v32/drivers/iop_fw_load.c @@ -1,5 +1,4 @@ -/* $Id: iop_fw_load.c,v 1.4 2005/04/07 09:27:46 larsv Exp $ - * +/* * Firmware loader for ETRAX FS IO-Processor * * Copyright (C) 2004 Axis Communications AB @@ -11,12 +10,13 @@ #include <linux/device.h> #include <linux/firmware.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/iop/iop_reg_space.h> -#include <asm/arch/hwregs/iop/iop_mpu_macros.h> -#include <asm/arch/hwregs/iop/iop_mpu_defs.h> -#include <asm/arch/hwregs/iop/iop_spu_defs.h> -#include <asm/arch/hwregs/iop/iop_sw_cpu_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/iop/iop_reg_space.h> +#include <hwregs/iop/iop_mpu_macros.h> +#include <hwregs/iop/iop_mpu_defs.h> +#include <hwregs/iop/iop_spu_defs.h> +#include <hwregs/iop/iop_sw_cpu_defs.h> #define IOP_TIMEOUT 100 diff --git a/arch/cris/arch-v32/drivers/mach-a3/Makefile b/arch/cris/arch-v32/drivers/mach-a3/Makefile new file mode 100644 index 00000000000..5c6d2a2a080 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Etrax-specific drivers +# + +obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o diff --git a/arch/cris/arch-v32/drivers/mach-a3/gpio.c b/arch/cris/arch-v32/drivers/mach-a3/gpio.c new file mode 100644 index 00000000000..de107dad9f4 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/gpio.c @@ -0,0 +1,984 @@ +/* + * Artec-3 general port I/O device + * + * Copyright (c) 2007 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Ola Knutsson (LED handling) + * Johan Adolfsson (read/set directions, write, port G, + * port to ETRAX FS. + * Ricard Wanderlof (PWM for Artpec-3) + * + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/poll.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> + +#include <asm/etraxgpio.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/intr_vect_defs.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/irq.h> +#include <asm/arch/mach/pinmux.h> + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +#include "../i2c.h" + +#define VIRT_I2C_ADDR 0x40 +#endif + +/* The following gio ports on ARTPEC-3 is available: + * pa 32 bits + * pb 32 bits + * pc 16 bits + * each port has a rw_px_dout, r_px_din and rw_px_oe register. + */ + +#define GPIO_MAJOR 120 /* experimental MAJOR number */ + +#define I2C_INTERRUPT_BITS 0x300 /* i2c0_done and i2c1_done bits */ + +#define D(x) + +#if 0 +static int dp_cnt; +#define DP(x) \ + do { \ + dp_cnt++; \ + if (dp_cnt % 1000 == 0) \ + x; \ + } while (0) +#else +#define DP(x) +#endif + +static char gpio_name[] = "etrax gpio"; + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off); +static int gpio_open(struct inode *inode, struct file *filp); +static int gpio_release(struct inode *inode, struct file *filp); +static unsigned int gpio_poll(struct file *filp, + struct poll_table_struct *wait); + +/* private data per open() of this driver */ + +struct gpio_private { + struct gpio_private *next; + /* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */ + unsigned char clk_mask; + unsigned char data_mask; + unsigned char write_msb; + unsigned char pad1; + /* These fields are generic */ + unsigned long highalarm, lowalarm; + wait_queue_head_t alarm_wq; + int minor; +}; + +static void gpio_set_alarm(struct gpio_private *priv); +static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg); +static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, + unsigned long arg); + + +/* linked list of alarms to check for */ + +static struct gpio_private *alarmlist; + +static int wanted_interrupts; + +static DEFINE_SPINLOCK(gpio_lock); + +#define NUM_PORTS (GPIO_MINOR_LAST+1) +#define GIO_REG_RD_ADDR(reg) \ + (unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) +#define GIO_REG_WR_ADDR(reg) \ + (unsigned long *)(regi_gio + REG_WR_ADDR_gio_##reg) +static unsigned long led_dummy; +static unsigned long port_d_dummy; /* Only input on Artpec-3 */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static unsigned long port_e_dummy; /* Non existent on Artpec-3 */ +static unsigned long virtual_dummy; +static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; +static unsigned short cached_virtual_gpio_read; +#endif + +static unsigned long *data_out[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_dout), + GIO_REG_WR_ADDR(rw_pb_dout), + &led_dummy, + GIO_REG_WR_ADDR(rw_pc_dout), + &port_d_dummy, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_dummy, +#endif +}; + +static unsigned long *data_in[NUM_PORTS] = { + GIO_REG_RD_ADDR(r_pa_din), + GIO_REG_RD_ADDR(r_pb_din), + &led_dummy, + GIO_REG_RD_ADDR(r_pc_din), + GIO_REG_RD_ADDR(r_pd_din), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_dummy, +#endif +}; + +static unsigned long changeable_dir[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_DIR, + CONFIG_ETRAX_PB_CHANGEABLE_DIR, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_DIR, + 0, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + 0, + CONFIG_ETRAX_PV_CHANGEABLE_DIR, +#endif +}; + +static unsigned long changeable_bits[NUM_PORTS] = { + CONFIG_ETRAX_PA_CHANGEABLE_BITS, + CONFIG_ETRAX_PB_CHANGEABLE_BITS, + 0, + CONFIG_ETRAX_PC_CHANGEABLE_BITS, + 0, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + 0, + CONFIG_ETRAX_PV_CHANGEABLE_BITS, +#endif +}; + +static unsigned long *dir_oe[NUM_PORTS] = { + GIO_REG_WR_ADDR(rw_pa_oe), + GIO_REG_WR_ADDR(rw_pb_oe), + &led_dummy, + GIO_REG_WR_ADDR(rw_pc_oe), + &port_d_dummy, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &port_e_dummy, + &virtual_rw_pv_oe, +#endif +}; + +static void gpio_set_alarm(struct gpio_private *priv) +{ + int bit; + int intr_cfg; + int mask; + int pins; + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + intr_cfg = REG_RD_INT(gio, regi_gio, rw_intr_cfg); + pins = REG_RD_INT(gio, regi_gio, rw_intr_pins); + mask = REG_RD_INT(gio, regi_gio, rw_intr_mask) & I2C_INTERRUPT_BITS; + + for (bit = 0; bit < 32; bit++) { + int intr = bit % 8; + int pin = bit / 8; + if (priv->minor < GPIO_MINOR_LEDS) + pin += priv->minor * 4; + else + pin += (priv->minor - 1) * 4; + + if (priv->highalarm & (1<<bit)) { + intr_cfg |= (regk_gio_hi << (intr * 3)); + mask |= 1 << intr; + wanted_interrupts = mask & 0xff; + pins |= pin << (intr * 4); + } else if (priv->lowalarm & (1<<bit)) { + intr_cfg |= (regk_gio_lo << (intr * 3)); + mask |= 1 << intr; + wanted_interrupts = mask & 0xff; + pins |= pin << (intr * 4); + } + } + + REG_WR_INT(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR_INT(gio, regi_gio, rw_intr_pins, pins); + REG_WR_INT(gio, regi_gio, rw_intr_mask, mask); + + spin_unlock_irqrestore(&gpio_lock, flags); +} + +static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct gpio_private *priv = file->private_data; + unsigned long data; + unsigned long tmp; + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return 0; + + poll_wait(file, &priv->alarm_wq, wait); + if (priv->minor <= GPIO_MINOR_D) { + data = readl(data_in[priv->minor]); + REG_WR_INT(gio, regi_gio, rw_ack_intr, wanted_interrupts); + tmp = REG_RD_INT(gio, regi_gio, rw_intr_mask); + tmp &= I2C_INTERRUPT_BITS; + tmp |= wanted_interrupts; + REG_WR_INT(gio, regi_gio, rw_intr_mask, tmp); + } else + return 0; + + if ((data & priv->highalarm) || (~data & priv->lowalarm)) + mask = POLLIN|POLLRDNORM; + + DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask)); + return mask; +} + +static irqreturn_t gpio_interrupt(int irq, void *dev_id) +{ + reg_gio_rw_intr_mask intr_mask; + reg_gio_r_masked_intr masked_intr; + reg_gio_rw_ack_intr ack_intr; + unsigned long flags; + unsigned long tmp; + unsigned long tmp2; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + unsigned char enable_gpiov_ack = 0; +#endif + + /* Find what PA interrupts are active */ + masked_intr = REG_RD(gio, regi_gio, r_masked_intr); + tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr); + + /* Find those that we have enabled */ + spin_lock_irqsave(&gpio_lock, flags); + tmp &= wanted_interrupts; + spin_unlock_irqrestore(&gpio_lock, flags); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Something changed on virtual GPIO. Interrupt is acked by + * reading the device. + */ + if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { + i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, + sizeof(cached_virtual_gpio_read)); + enable_gpiov_ack = 1; + } +#endif + + /* Ack them */ + ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); + REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); + + /* Disable those interrupts.. */ + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); + tmp2 &= ~tmp; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Do not disable interrupt on virtual GPIO. Changes on virtual + * pins are only noticed by an interrupt. + */ + if (enable_gpiov_ack) + tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + return IRQ_RETVAL(tmp); +} + +static void gpio_write_bit(unsigned long *port, unsigned char data, int bit, + unsigned char clk_mask, unsigned char data_mask) +{ + unsigned long shadow = readl(port) & ~clk_mask; + writel(shadow, port); + if (data & 1 << bit) + shadow |= data_mask; + else + shadow &= ~data_mask; + writel(shadow, port); + /* For FPGA: min 5.0ns (DCC) before CCLK high */ + shadow |= clk_mask; + writel(shadow, port); +} + +static void gpio_write_byte(struct gpio_private *priv, unsigned long *port, + unsigned char data) +{ + int i; + + if (priv->write_msb) + for (i = 7; i >= 0; i--) + gpio_write_bit(port, data, i, priv->clk_mask, + priv->data_mask); + else + for (i = 0; i <= 7; i++) + gpio_write_bit(port, data, i, priv->clk_mask, + priv->data_mask); +} + + +static ssize_t gpio_write(struct file *file, const char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_private *priv = file->private_data; + unsigned long flags; + ssize_t retval = count; + /* Only bits 0-7 may be used for write operations but allow all + devices except leds... */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return -EFAULT; +#endif + if (priv->minor == GPIO_MINOR_LEDS) + return -EFAULT; + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return -EFAULT; + + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + + /* It must have been configured using the IO_CFG_WRITE_MODE */ + /* Perhaps a better error code? */ + if (priv->clk_mask == 0 || priv->data_mask == 0) + return -EPERM; + + D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X " + "msb: %i\n", + count, priv->data_mask, priv->clk_mask, priv->write_msb)); + + spin_lock_irqsave(&gpio_lock, flags); + + while (count--) + gpio_write_byte(priv, data_out[priv->minor], *buf++); + + spin_unlock_irqrestore(&gpio_lock, flags); + return retval; +} + +static int gpio_open(struct inode *inode, struct file *filp) +{ + struct gpio_private *priv; + int p = iminor(inode); + + if (p > GPIO_MINOR_LAST_PWM || + (p > GPIO_MINOR_LAST && p < GPIO_MINOR_PWM0)) + return -EINVAL; + + priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); + + if (!priv) + return -ENOMEM; + memset(priv, 0, sizeof(*priv)); + + priv->minor = p; + filp->private_data = priv; + + /* initialize the io/alarm struct, not for PWM ports though */ + if (p <= GPIO_MINOR_LAST) { + + priv->clk_mask = 0; + priv->data_mask = 0; + priv->highalarm = 0; + priv->lowalarm = 0; + + init_waitqueue_head(&priv->alarm_wq); + + /* link it into our alarmlist */ + spin_lock_irq(&gpio_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&gpio_lock); + } + + return 0; +} + +static int gpio_release(struct inode *inode, struct file *filp) +{ + struct gpio_private *p; + struct gpio_private *todel; + /* local copies while updating them: */ + unsigned long a_high, a_low; + + /* prepare to free private structure */ + todel = filp->private_data; + + /* unlink from alarmlist - only for non-PWM ports though */ + if (todel->minor <= GPIO_MINOR_LAST) { + spin_lock_irq(&gpio_lock); + p = alarmlist; + + if (p == todel) + alarmlist = todel->next; + else { + while (p->next != todel) + p = p->next; + p->next = todel->next; + } + + /* Check if there are still any alarms set */ + p = alarmlist; + a_high = 0; + a_low = 0; + while (p) { + if (p->minor == GPIO_MINOR_A) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + a_high |= p->highalarm; + a_low |= p->lowalarm; + } + + p = p->next; + } + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Variable 'a_low' needs to be set here again + * to ensure that interrupt for virtual GPIO is handled. + */ + a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + + spin_unlock_irq(&gpio_lock); + } + kfree(todel); + + return 0; +} + +/* Main device API. ioctl's to read/set/clear bits, as well as to + * set alarms to wait for using a subsequent select(). + */ + +inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg) +{ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + unsigned long flags; + unsigned long dir_shadow; + + spin_lock_irqsave(&gpio_lock, flags); + + dir_shadow = readl(dir_oe[priv->minor]) & + ~(arg & changeable_dir[priv->minor]); + writel(dir_shadow, dir_oe[priv->minor]); + + spin_unlock_irqrestore(&gpio_lock, flags); + + if (priv->minor == GPIO_MINOR_C) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + else if (priv->minor == GPIO_MINOR_V) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#endif + else + dir_shadow ^= 0xFFFFFFFF; /* PA, PB and PD 32 bits */ + + return dir_shadow; + +} /* setget_input */ + +static inline unsigned long setget_output(struct gpio_private *priv, + unsigned long arg) +{ + unsigned long flags; + unsigned long dir_shadow; + + spin_lock_irqsave(&gpio_lock, flags); + + dir_shadow = readl(dir_oe[priv->minor]) | + (arg & changeable_dir[priv->minor]); + writel(dir_shadow, dir_oe[priv->minor]); + + spin_unlock_irqrestore(&gpio_lock, flags); + return dir_shadow; +} /* setget_output */ + +static int gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned long val; + unsigned long shadow; + struct gpio_private *priv = file->private_data; + + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) + return -ENOTTY; + + /* Check for special ioctl handlers first */ + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return virtual_gpio_ioctl(file, cmd, arg); +#endif + + if (priv->minor == GPIO_MINOR_LEDS) + return gpio_leds_ioctl(cmd, arg); + + if (priv->minor >= GPIO_MINOR_PWM0 && + priv->minor <= GPIO_MINOR_LAST_PWM) + return gpio_pwm_ioctl(priv, cmd, arg); + + switch (_IOC_NR(cmd)) { + case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ + /* Read the port. */ + return readl(data_in[priv->minor]); + case IO_SETBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Set changeable bits with a 1 in arg. */ + shadow = readl(data_out[priv->minor]) | + (arg & changeable_bits[priv->minor]); + writel(shadow, data_out[priv->minor]); + spin_unlock_irqrestore(&gpio_lock, flags); + break; + case IO_CLRBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Clear changeable bits with a 1 in arg. */ + shadow = readl(data_out[priv->minor]) & + ~(arg & changeable_bits[priv->minor]); + writel(shadow, data_out[priv->minor]); + spin_unlock_irqrestore(&gpio_lock, flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + gpio_set_alarm(priv); + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + gpio_set_alarm(priv); + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + gpio_set_alarm(priv); + break; + case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ + /* Read direction 0=input 1=output */ + return readl(dir_oe[priv->minor]); + + case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ + /* Set direction 0=unchanged 1=input, + * return mask with 1=input + */ + return setget_input(priv, arg); + + case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ + /* Set direction 0=unchanged 1=output, + * return mask with 1=output + */ + return setget_output(priv, arg); + + case IO_CFG_WRITE_MODE: + { + int res = -EPERM; + unsigned long dir_shadow, clk_mask, data_mask, write_msb; + + clk_mask = arg & 0xFF; + data_mask = (arg >> 8) & 0xFF; + write_msb = (arg >> 16) & 0x01; + + /* Check if we're allowed to change the bits and + * the direction is correct + */ + spin_lock_irqsave(&gpio_lock, flags); + dir_shadow = readl(dir_oe[priv->minor]); + if ((clk_mask & changeable_bits[priv->minor]) && + (data_mask & changeable_bits[priv->minor]) && + (clk_mask & dir_shadow) && + (data_mask & dir_shadow)) { + priv->clk_mask = clk_mask; + priv->data_mask = data_mask; + priv->write_msb = write_msb; + res = 0; + } + spin_unlock_irqrestore(&gpio_lock, flags); + + return res; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = readl(data_in[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + val = *data_out[priv->minor]; + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned long flags; + unsigned short val; + unsigned short shadow; + struct gpio_private *priv = file->private_data; + + switch (_IOC_NR(cmd)) { + case IO_SETBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Set changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~readl(dir_oe[priv->minor]) | + (arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + spin_lock_irqrestore(&gpio_lock, flags); + break; + case IO_CLRBITS: + spin_lock_irqsave(&gpio_lock, flags); + /* Clear changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~readl(dir_oe[priv->minor]) & + ~(arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + spin_lock_irqrestore(&gpio_lock, flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + break; + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = readl(dir_oe[priv->minor]); + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = cached_virtual_gpio_read & ~readl(dir_oe[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); + val &= readl(dir_oe[priv->minor]); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + { + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + unsigned short input_mask = ~readl(dir_oe[priv->minor]); + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + if ((input_mask & val) != input_mask) { + /* Input pins changed. All ports desired as input + * should be set to logic 1. + */ + unsigned short change = input_mask ^ val; + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + shadow &= ~change; + shadow |= val; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + } + break; + } + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (void __user *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((void __user *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + return 0; +} +#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ + +static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) +{ + unsigned char green; + unsigned char red; + + switch (_IOC_NR(cmd)) { + case IO_LEDACTIVE_SET: + green = ((unsigned char) arg) & 1; + red = (((unsigned char) arg) >> 1) & 1; + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); + break; + + default: + return -EINVAL; + } /* switch */ + + return 0; +} + +static int gpio_pwm_set_mode(unsigned long arg, int pwm_port) +{ + int pinmux_pwm = pinmux_pwm0 + pwm_port; + int mode; + reg_gio_rw_pwm0_ctrl rw_pwm_ctrl = { + .ccd_val = 0, + .ccd_override = regk_gio_no, + .mode = regk_gio_no + }; + int allocstatus; + + if (get_user(mode, &((struct io_pwm_set_mode *) arg)->mode)) + return -EFAULT; + rw_pwm_ctrl.mode = mode; + if (mode != PWM_OFF) + allocstatus = crisv32_pinmux_alloc_fixed(pinmux_pwm); + else + allocstatus = crisv32_pinmux_dealloc_fixed(pinmux_pwm); + if (allocstatus) + return allocstatus; + REG_WRITE(reg_gio_rw_pwm0_ctrl, REG_ADDR(gio, regi_gio, rw_pwm0_ctrl) + + 12 * pwm_port, rw_pwm_ctrl); + return 0; +} + +static int gpio_pwm_set_period(unsigned long arg, int pwm_port) +{ + struct io_pwm_set_period periods; + reg_gio_rw_pwm0_var rw_pwm_widths; + + if (copy_from_user(&periods, (void __user *)arg, sizeof(periods))) + return -EFAULT; + if (periods.lo > 8191 || periods.hi > 8191) + return -EINVAL; + rw_pwm_widths.lo = periods.lo; + rw_pwm_widths.hi = periods.hi; + REG_WRITE(reg_gio_rw_pwm0_var, REG_ADDR(gio, regi_gio, rw_pwm0_var) + + 12 * pwm_port, rw_pwm_widths); + return 0; +} + +static int gpio_pwm_set_duty(unsigned long arg, int pwm_port) +{ + unsigned int duty; + reg_gio_rw_pwm0_data rw_pwm_duty; + + if (get_user(duty, &((struct io_pwm_set_duty *) arg)->duty)) + return -EFAULT; + if (duty > 255) + return -EINVAL; + rw_pwm_duty.data = duty; + REG_WRITE(reg_gio_rw_pwm0_data, REG_ADDR(gio, regi_gio, rw_pwm0_data) + + 12 * pwm_port, rw_pwm_duty); + return 0; +} + +static int gpio_pwm_ioctl(struct gpio_private *priv, unsigned int cmd, + unsigned long arg) +{ + int pwm_port = priv->minor - GPIO_MINOR_PWM0; + + switch (_IOC_NR(cmd)) { + case IO_PWM_SET_MODE: + return gpio_pwm_set_mode(arg, pwm_port); + case IO_PWM_SET_PERIOD: + return gpio_pwm_set_period(arg, pwm_port); + case IO_PWM_SET_DUTY: + return gpio_pwm_set_duty(arg, pwm_port); + default: + return -EINVAL; + } + return 0; +} + +static const struct file_operations gpio_fops = { + .owner = THIS_MODULE, + .poll = gpio_poll, + .ioctl = gpio_ioctl, + .write = gpio_write, + .open = gpio_open, + .release = gpio_release, +}; + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static void __init virtual_gpio_init(void) +{ + reg_gio_rw_intr_cfg intr_cfg; + reg_gio_rw_intr_mask intr_mask; + unsigned short shadow; + + shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ + shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + + /* Set interrupt mask and on what state the interrupt shall trigger. + * For virtual gpio the interrupt shall trigger on logic '0'. + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + + switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { + case 0: + intr_cfg.pa0 = regk_gio_lo; + intr_mask.pa0 = regk_gio_yes; + break; + case 1: + intr_cfg.pa1 = regk_gio_lo; + intr_mask.pa1 = regk_gio_yes; + break; + case 2: + intr_cfg.pa2 = regk_gio_lo; + intr_mask.pa2 = regk_gio_yes; + break; + case 3: + intr_cfg.pa3 = regk_gio_lo; + intr_mask.pa3 = regk_gio_yes; + break; + case 4: + intr_cfg.pa4 = regk_gio_lo; + intr_mask.pa4 = regk_gio_yes; + break; + case 5: + intr_cfg.pa5 = regk_gio_lo; + intr_mask.pa5 = regk_gio_yes; + break; + case 6: + intr_cfg.pa6 = regk_gio_lo; + intr_mask.pa6 = regk_gio_yes; + break; + case 7: + intr_cfg.pa7 = regk_gio_lo; + intr_mask.pa7 = regk_gio_yes; + break; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); +} +#endif + +/* main driver initialization routine, called from mem.c */ + +static int __init gpio_init(void) +{ + int res; + + printk(KERN_INFO "ETRAX FS GPIO driver v2.7, (c) 2003-2008 " + "Axis Communications AB\n"); + + /* do the formalities */ + + res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); + if (res < 0) { + printk(KERN_ERR "gpio: couldn't get a major number.\n"); + return res; + } + + /* Clear all leds */ + CRIS_LED_NETWORK_GRP0_SET(0); + CRIS_LED_NETWORK_GRP1_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); + + int res2 = request_irq(GIO_INTR_VECT, gpio_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio", &alarmlist); + if (res2) { + printk(KERN_ERR "err: irq for gpio\n"); + return res2; + } + + /* No IRQs by default. */ + REG_WR_INT(gio, regi_gio, rw_intr_pins, 0); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + virtual_gpio_init(); +#endif + + return res; +} + +/* this makes sure that gpio_init is called during kernel boot */ + +module_init(gpio_init); diff --git a/arch/cris/arch-v32/drivers/mach-a3/nandflash.c b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c new file mode 100644 index 00000000000..01ed0be2d0d --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-a3/nandflash.c @@ -0,0 +1,180 @@ +/* + * arch/cris/arch-v32/drivers/nandflash.c + * + * Copyright (c) 2007 + * + * Derived from drivers/mtd/nand/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/arch/memmap.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/pio_defs.h> +#include <pinmux.h> +#include <asm/io.h> + +#define MANUAL_ALE_CLE_CONTROL 1 + +#define regf_ALE a0 +#define regf_CLE a1 +#define regf_NCE ce0_n + +#define CLE_BIT 10 +#define ALE_BIT 11 +#define CE_BIT 12 + +struct mtd_info_wrapper { + struct mtd_info info; + struct nand_chip chip; +}; + +/* Bitmask for control pins */ +#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT)) + +static struct mtd_info *crisv32_mtd; +/* + * hardware specific access to control-lines + */ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + unsigned long flags; + reg_pio_rw_dout dout; + struct nand_chip *this = mtd->priv; + + local_irq_save(flags); + + /* control bits change */ + if (ctrl & NAND_CTRL_CHANGE) { + dout = REG_RD(pio, regi_pio, rw_dout); + dout.regf_NCE = (ctrl & NAND_NCE) ? 0 : 1; + +#if !MANUAL_ALE_CLE_CONTROL + if (ctrl & NAND_ALE) { + /* A0 = ALE high */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access1); + } else if (ctrl & NAND_CLE) { + /* A1 = CLE high */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access2); + } else { + /* A1 = CLE and A0 = ALE low */ + this->IO_ADDR_W = (void __iomem *)REG_ADDR(pio, + regi_pio, rw_io_access0); + } +#else + + dout.regf_CLE = (ctrl & NAND_CLE) ? 1 : 0; + dout.regf_ALE = (ctrl & NAND_ALE) ? 1 : 0; +#endif + REG_WR(pio, regi_pio, rw_dout, dout); + } + + /* command to chip */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); + + local_irq_restore(flags); +} + +/* +* read device ready pin +*/ +static int crisv32_device_ready(struct mtd_info *mtd) +{ + reg_pio_r_din din = REG_RD(pio, regi_pio, r_din); + return din.rdy; +} + +/* + * Main initialization routine + */ +struct mtd_info *__init crisv32_nand_flash_probe(void) +{ + void __iomem *read_cs; + void __iomem *write_cs; + + struct mtd_info_wrapper *wrapper; + struct nand_chip *this; + int err = 0; + + reg_pio_rw_man_ctrl man_ctrl = { + .regf_NCE = regk_pio_yes, +#if MANUAL_ALE_CLE_CONTROL + .regf_ALE = regk_pio_yes, + .regf_CLE = regk_pio_yes +#endif + }; + reg_pio_rw_oe oe = { + .regf_NCE = regk_pio_yes, +#if MANUAL_ALE_CLE_CONTROL + .regf_ALE = regk_pio_yes, + .regf_CLE = regk_pio_yes +#endif + }; + reg_pio_rw_dout dout = { .regf_NCE = 1 }; + + /* Allocate pio pins to pio */ + crisv32_pinmux_alloc_fixed(pinmux_pio); + /* Set up CE, ALE, CLE (ce0_n, a0, a1) for manual control and output */ + REG_WR(pio, regi_pio, rw_man_ctrl, man_ctrl); + REG_WR(pio, regi_pio, rw_dout, dout); + REG_WR(pio, regi_pio, rw_oe, oe); + + /* Allocate memory for MTD device structure and private data */ + wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL); + if (!wrapper) { + printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD " + "device structure.\n"); + err = -ENOMEM; + return NULL; + } + + read_cs = write_cs = (void __iomem *)REG_ADDR(pio, regi_pio, + rw_io_access0); + + /* Get pointer to private data */ + this = &wrapper->chip; + crisv32_mtd = &wrapper->info; + + /* Link the private data with the MTD structure */ + crisv32_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = read_cs; + this->IO_ADDR_W = write_cs; + this->cmd_ctrl = crisv32_hwcontrol; + this->dev_ready = crisv32_device_ready; + /* 20 us command delay time */ + this->chip_delay = 20; + this->ecc.mode = NAND_ECC_SOFT; + + /* Enable the following for a flash based bad block table */ + /* this->options = NAND_USE_FLASH_BBT; */ + + /* Scan to find existance of the device */ + if (nand_scan(crisv32_mtd, 1)) { + err = -ENXIO; + goto out_mtd; + } + + return crisv32_mtd; + +out_mtd: + kfree(wrapper); + return NULL; +} + diff --git a/arch/cris/arch-v32/drivers/mach-fs/Makefile b/arch/cris/arch-v32/drivers/mach-fs/Makefile new file mode 100644 index 00000000000..5c6d2a2a080 --- /dev/null +++ b/arch/cris/arch-v32/drivers/mach-fs/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for Etrax-specific drivers +# + +obj-$(CONFIG_ETRAX_NANDFLASH) += nandflash.o +obj-$(CONFIG_ETRAX_GPIO) += gpio.o diff --git a/arch/cris/arch-v32/drivers/gpio.c b/arch/cris/arch-v32/drivers/mach-fs/gpio.c index d82c5c56113..7863fd4efc2 100644 --- a/arch/cris/arch-v32/drivers/gpio.c +++ b/arch/cris/arch-v32/drivers/mach-fs/gpio.c @@ -1,68 +1,15 @@ -/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $ - * +/* * ETRAX CRISv32 general port I/O device * - * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB + * Copyright (c) 1999-2006 Axis Communications AB * * Authors: Bjorn Wesen (initial version) * Ola Knutsson (LED handling) * Johan Adolfsson (read/set directions, write, port G, * port to ETRAX FS. * - * $Log: gpio.c,v $ - * Revision 1.16 2005/06/19 17:06:49 starvik - * Merge of Linux 2.6.12. - * - * Revision 1.15 2005/05/25 08:22:20 starvik - * Changed GPIO port order to fit packages/devices/axis-2.4. - * - * Revision 1.14 2005/04/24 18:35:08 starvik - * Updated with final register headers. - * - * Revision 1.13 2005/03/15 15:43:00 starvik - * dev_id needs to be supplied for shared IRQs. - * - * Revision 1.12 2005/03/10 17:12:00 starvik - * Protect alarm list with spinlock. - * - * Revision 1.11 2005/01/05 06:08:59 starvik - * No need to do local_irq_disable after local_irq_save. - * - * Revision 1.10 2004/11/19 08:38:31 starvik - * Removed old crap. - * - * Revision 1.9 2004/05/14 07:58:02 starvik - * Merge of changes from 2.4 - * - * Revision 1.8 2003/09/11 07:29:50 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.7 2003/07/10 13:25:46 starvik - * Compiles for 2.5.74 - * Lindented ethernet.c - * - * Revision 1.6 2003/07/04 08:27:46 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/10 08:26:37 johana - * Etrax -> ETRAX CRISv32 - * - * Revision 1.4 2003/06/05 14:22:48 johana - * Initialise some_alarms. - * - * Revision 1.3 2003/06/05 10:15:46 johana - * New INTR_VECT macros. - * Enable interrupts in global config. - * - * Revision 1.2 2003/06/03 15:52:50 johana - * Initial CRIS v32 version. - * - * Revision 1.1 2003/06/03 08:53:15 johana - * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7. - * */ - #include <linux/module.h> #include <linux/sched.h> #include <linux/slab.h> @@ -77,14 +24,20 @@ #include <linux/spinlock.h> #include <asm/etraxgpio.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/gio_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/intr_vect_defs.h> #include <asm/io.h> #include <asm/system.h> #include <asm/irq.h> +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +#include "../i2c.h" + +#define VIRT_I2C_ADDR 0x40 +#endif + /* The following gio ports on ETRAX FS is available: * pa 8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge * pb 18 bits @@ -100,7 +53,12 @@ #if 0 static int dp_cnt; -#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0) +#define DP(x) \ + do { \ + dp_cnt++; \ + if (dp_cnt % 1000 == 0) \ + x; \ + } while (0) #else #define DP(x) #endif @@ -111,13 +69,18 @@ static char gpio_name[] = "etrax gpio"; static wait_queue_head_t *gpio_wq; #endif +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int virtual_gpio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +#endif static int gpio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off); + unsigned int cmd, unsigned long arg); +static ssize_t gpio_write(struct file *file, const char *buf, size_t count, + loff_t *off); static int gpio_open(struct inode *inode, struct file *filp); static int gpio_release(struct inode *inode, struct file *filp); -static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); +static unsigned int gpio_poll(struct file *filp, + struct poll_table_struct *wait); /* private data per open() of this driver */ @@ -136,18 +99,25 @@ struct gpio_private { /* linked list of alarms to check for */ -static struct gpio_private *alarmlist = 0; +static struct gpio_private *alarmlist; -static int gpio_some_alarms = 0; /* Set if someone uses alarm */ -static unsigned long gpio_pa_high_alarms = 0; -static unsigned long gpio_pa_low_alarms = 0; +static int gpio_some_alarms; /* Set if someone uses alarm */ +static unsigned long gpio_pa_high_alarms; +static unsigned long gpio_pa_low_alarms; static DEFINE_SPINLOCK(alarm_lock); #define NUM_PORTS (GPIO_MINOR_LAST+1) -#define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) -#define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg ) +#define GIO_REG_RD_ADDR(reg) \ + (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) +#define GIO_REG_WR_ADDR(reg) \ + (volatile unsigned long *)(regi_gio + REG_RD_ADDR_gio_##reg) unsigned long led_dummy; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static unsigned long virtual_dummy; +static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE; +static unsigned short cached_virtual_gpio_read; +#endif static volatile unsigned long *data_out[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pa_dout), @@ -156,6 +126,9 @@ static volatile unsigned long *data_out[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pc_dout), GIO_REG_WR_ADDR(rw_pd_dout), GIO_REG_WR_ADDR(rw_pe_dout), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; static volatile unsigned long *data_in[NUM_PORTS] = { @@ -165,6 +138,9 @@ static volatile unsigned long *data_in[NUM_PORTS] = { GIO_REG_RD_ADDR(r_pc_din), GIO_REG_RD_ADDR(r_pd_din), GIO_REG_RD_ADDR(r_pe_din), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_dummy, +#endif }; static unsigned long changeable_dir[NUM_PORTS] = { @@ -174,6 +150,9 @@ static unsigned long changeable_dir[NUM_PORTS] = { CONFIG_ETRAX_PC_CHANGEABLE_DIR, CONFIG_ETRAX_PD_CHANGEABLE_DIR, CONFIG_ETRAX_PE_CHANGEABLE_DIR, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_DIR, +#endif }; static unsigned long changeable_bits[NUM_PORTS] = { @@ -183,6 +162,9 @@ static unsigned long changeable_bits[NUM_PORTS] = { CONFIG_ETRAX_PC_CHANGEABLE_BITS, CONFIG_ETRAX_PD_CHANGEABLE_BITS, CONFIG_ETRAX_PE_CHANGEABLE_BITS, +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + CONFIG_ETRAX_PV_CHANGEABLE_BITS, +#endif }; static volatile unsigned long *dir_oe[NUM_PORTS] = { @@ -192,13 +174,14 @@ static volatile unsigned long *dir_oe[NUM_PORTS] = { GIO_REG_WR_ADDR(rw_pc_oe), GIO_REG_WR_ADDR(rw_pd_oe), GIO_REG_WR_ADDR(rw_pe_oe), +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + &virtual_rw_pv_oe, +#endif }; -static unsigned int -gpio_poll(struct file *file, - poll_table *wait) +static unsigned int gpio_poll(struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; struct gpio_private *priv = (struct gpio_private *)file->private_data; @@ -210,65 +193,50 @@ gpio_poll(struct file *file, unsigned long flags; local_irq_save(flags); - data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, REG_RD(gio, regi_gio, r_pa_din)); + data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, + REG_RD(gio, regi_gio, r_pa_din)); /* PA has support for interrupt * lets activate high for those low and with highalarm set */ intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); tmp = ~data & priv->highalarm & 0xFF; - if (tmp & (1 << 0)) { + if (tmp & (1 << 0)) intr_cfg.pa0 = regk_gio_hi; - } - if (tmp & (1 << 1)) { + if (tmp & (1 << 1)) intr_cfg.pa1 = regk_gio_hi; - } - if (tmp & (1 << 2)) { + if (tmp & (1 << 2)) intr_cfg.pa2 = regk_gio_hi; - } - if (tmp & (1 << 3)) { + if (tmp & (1 << 3)) intr_cfg.pa3 = regk_gio_hi; - } - if (tmp & (1 << 4)) { + if (tmp & (1 << 4)) intr_cfg.pa4 = regk_gio_hi; - } - if (tmp & (1 << 5)) { + if (tmp & (1 << 5)) intr_cfg.pa5 = regk_gio_hi; - } - if (tmp & (1 << 6)) { + if (tmp & (1 << 6)) intr_cfg.pa6 = regk_gio_hi; - } - if (tmp & (1 << 7)) { + if (tmp & (1 << 7)) intr_cfg.pa7 = regk_gio_hi; - } /* * lets activate low for those high and with lowalarm set */ tmp = data & priv->lowalarm & 0xFF; - if (tmp & (1 << 0)) { + if (tmp & (1 << 0)) intr_cfg.pa0 = regk_gio_lo; - } - if (tmp & (1 << 1)) { + if (tmp & (1 << 1)) intr_cfg.pa1 = regk_gio_lo; - } - if (tmp & (1 << 2)) { + if (tmp & (1 << 2)) intr_cfg.pa2 = regk_gio_lo; - } - if (tmp & (1 << 3)) { + if (tmp & (1 << 3)) intr_cfg.pa3 = regk_gio_lo; - } - if (tmp & (1 << 4)) { + if (tmp & (1 << 4)) intr_cfg.pa4 = regk_gio_lo; - } - if (tmp & (1 << 5)) { + if (tmp & (1 << 5)) intr_cfg.pa5 = regk_gio_lo; - } - if (tmp & (1 << 6)) { + if (tmp & (1 << 6)) intr_cfg.pa6 = regk_gio_lo; - } - if (tmp & (1 << 7)) { + if (tmp & (1 << 7)) intr_cfg.pa7 = regk_gio_lo; - } REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); local_irq_restore(flags); @@ -277,50 +245,65 @@ gpio_poll(struct file *file, else return 0; - if ((data & priv->highalarm) || - (~data & priv->lowalarm)) { + if ((data & priv->highalarm) || (~data & priv->lowalarm)) mask = POLLIN|POLLRDNORM; - } - DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); + DP(printk(KERN_DEBUG "gpio_poll ready: mask 0x%08X\n", mask)); return mask; } int etrax_gpio_wake_up_check(void) { - struct gpio_private *priv = alarmlist; + struct gpio_private *priv; unsigned long data = 0; - int ret = 0; + unsigned long flags; + int ret = 0; + spin_lock_irqsave(&alarm_lock, flags); + priv = alarmlist; while (priv) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + data = (unsigned long)cached_virtual_gpio_read; + else { + data = *data_in[priv->minor]; + if (priv->minor == GPIO_MINOR_A) + priv->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + } +#else data = *data_in[priv->minor]; +#endif if ((data & priv->highalarm) || (~data & priv->lowalarm)) { - DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); + DP(printk(KERN_DEBUG + "etrax_gpio_wake_up_check %i\n", priv->minor)); wake_up_interruptible(&priv->alarm_wq); - ret = 1; + ret = 1; } priv = priv->next; } - return ret; + spin_unlock_irqrestore(&alarm_lock, flags); + return ret; } static irqreturn_t -gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_poll_timer_interrupt(int irq, void *dev_id) { - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } - return IRQ_NONE; + return IRQ_NONE; } static irqreturn_t -gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +gpio_pa_interrupt(int irq, void *dev_id) { reg_gio_rw_intr_mask intr_mask; reg_gio_r_masked_intr masked_intr; reg_gio_rw_ack_intr ack_intr; unsigned long tmp; unsigned long tmp2; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + unsigned char enable_gpiov_ack = 0; +#endif /* Find what PA interrupts are active */ masked_intr = REG_RD(gio, regi_gio, r_masked_intr); @@ -331,6 +314,17 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms); spin_unlock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Something changed on virtual GPIO. Interrupt is acked by + * reading the device. + */ + if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) { + i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read, + sizeof(cached_virtual_gpio_read)); + enable_gpiov_ack = 1; + } +#endif + /* Ack them */ ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp); REG_WR(gio, regi_gio, rw_ack_intr, ack_intr); @@ -339,18 +333,24 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask); tmp2 &= ~tmp; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Do not disable interrupt on virtual GPIO. Changes on virtual + * pins are only noticed by an interrupt. + */ + if (enable_gpiov_ack) + tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2); REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); - if (gpio_some_alarms) { + if (gpio_some_alarms) return IRQ_RETVAL(etrax_gpio_wake_up_check()); - } - return IRQ_NONE; + return IRQ_NONE; } -static ssize_t gpio_write(struct file * file, const char * buf, size_t count, - loff_t *off) +static ssize_t gpio_write(struct file *file, const char *buf, size_t count, + loff_t *off) { struct gpio_private *priv = (struct gpio_private *)file->private_data; unsigned char data, clk_mask, data_mask, write_msb; @@ -360,29 +360,31 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count, ssize_t retval = count; /* Only bits 0-7 may be used for write operations but allow all devices except leds... */ - if (priv->minor == GPIO_MINOR_LEDS) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return -EFAULT; +#endif + if (priv->minor == GPIO_MINOR_LEDS) return -EFAULT; - } - if (!access_ok(VERIFY_READ, buf, count)) { + if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; - } clk_mask = priv->clk_mask; data_mask = priv->data_mask; /* It must have been configured using the IO_CFG_WRITE_MODE */ /* Perhaps a better error code? */ - if (clk_mask == 0 || data_mask == 0) { + if (clk_mask == 0 || data_mask == 0) return -EPERM; - } write_msb = priv->write_msb; - D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); + D(printk(KERN_DEBUG "gpio_write: %lu to data 0x%02X clk 0x%02X " + "msb: %i\n", count, data_mask, clk_mask, write_msb)); port = data_out[priv->minor]; while (count--) { int i; data = *buf++; if (priv->write_msb) { - for (i = 7; i >= 0;i--) { + for (i = 7; i >= 0; i--) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; @@ -395,7 +397,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count, local_irq_restore(flags); } } else { - for (i = 0; i <= 7;i++) { + for (i = 0; i <= 7; i++) { local_irq_save(flags); shadow = *port; *port = shadow &= ~clk_mask; @@ -423,18 +425,16 @@ gpio_open(struct inode *inode, struct file *filp) if (p > GPIO_MINOR_LAST) return -EINVAL; - priv = kmalloc(sizeof(struct gpio_private), - GFP_KERNEL); + priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL); if (!priv) return -ENOMEM; + memset(priv, 0, sizeof(*priv)); priv->minor = p; - /* initialize the io/alarm struct and link it into our alarmlist */ + /* initialize the io/alarm struct */ - priv->next = alarmlist; - alarmlist = priv; priv->clk_mask = 0; priv->data_mask = 0; priv->highalarm = 0; @@ -443,20 +443,30 @@ gpio_open(struct inode *inode, struct file *filp) filp->private_data = (void *)priv; + /* link it into our alarmlist */ + spin_lock_irq(&alarm_lock); + priv->next = alarmlist; + alarmlist = priv; + spin_unlock_irq(&alarm_lock); + return 0; } static int gpio_release(struct inode *inode, struct file *filp) { - struct gpio_private *p = alarmlist; - struct gpio_private *todel = (struct gpio_private *)filp->private_data; + struct gpio_private *p; + struct gpio_private *todel; /* local copies while updating them: */ unsigned long a_high, a_low; unsigned long some_alarms; /* unlink from alarmlist and free the private structure */ + spin_lock_irq(&alarm_lock); + p = alarmlist; + todel = (struct gpio_private *)filp->private_data; + if (p == todel) { alarmlist = todel->next; } else { @@ -468,26 +478,35 @@ gpio_release(struct inode *inode, struct file *filp) kfree(todel); /* Check if there are still any alarms set */ p = alarmlist; - some_alarms = 0; + some_alarms = 0; a_high = 0; a_low = 0; while (p) { if (p->minor == GPIO_MINOR_A) { +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif a_high |= p->highalarm; a_low |= p->lowalarm; } - if (p->highalarm | p->lowalarm) { + if (p->highalarm | p->lowalarm) some_alarms = 1; - } p = p->next; } - spin_lock(&alarm_lock); +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + /* Variables 'some_alarms' and 'a_low' needs to be set here again + * to ensure that interrupt for virtual GPIO is handled. + */ + some_alarms = 1; + a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); +#endif + gpio_some_alarms = some_alarms; gpio_pa_high_alarms = a_high; gpio_pa_low_alarms = a_low; - spin_unlock(&alarm_lock); + spin_unlock_irq(&alarm_lock); return 0; } @@ -496,7 +515,7 @@ gpio_release(struct inode *inode, struct file *filp) * set alarms to wait for using a subsequent select(). */ -unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) +inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg) { /* Set direction 0=unchanged 1=input, * return mask with 1=input @@ -512,13 +531,17 @@ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) if (priv->minor == GPIO_MINOR_A) dir_shadow ^= 0xFF; /* Only 8 bits */ +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + else if (priv->minor == GPIO_MINOR_V) + dir_shadow ^= 0xFFFF; /* Only 16 bits */ +#endif else dir_shadow ^= 0x3FFFF; /* Only 18 bits */ return dir_shadow; } /* setget_input */ -unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) +inline unsigned long setget_output(struct gpio_private *priv, unsigned long arg) { unsigned long flags; unsigned long dir_shadow; @@ -542,20 +565,22 @@ gpio_ioctl(struct inode *inode, struct file *file, unsigned long val; unsigned long shadow; struct gpio_private *priv = (struct gpio_private *)file->private_data; - if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { + if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) return -EINVAL; - } + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + if (priv->minor == GPIO_MINOR_V) + return virtual_gpio_ioctl(file, cmd, arg); +#endif switch (_IOC_NR(cmd)) { case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ - // read the port + /* Read the port. */ return *data_in[priv->minor]; break; case IO_SETBITS: local_irq_save(flags); - if (arg & 0x04) - printk("GPIO SET 2\n"); - // set changeable bits with a 1 in arg + /* Set changeable bits with a 1 in arg. */ shadow = *data_out[priv->minor]; shadow |= (arg & changeable_bits[priv->minor]); *data_out[priv->minor] = shadow; @@ -563,46 +588,42 @@ gpio_ioctl(struct inode *inode, struct file *file, break; case IO_CLRBITS: local_irq_save(flags); - if (arg & 0x04) - printk("GPIO CLR 2\n"); - // clear changeable bits with a 1 in arg + /* Clear changeable bits with a 1 in arg. */ shadow = *data_out[priv->minor]; shadow &= ~(arg & changeable_bits[priv->minor]); *data_out[priv->minor] = shadow; local_irq_restore(flags); break; case IO_HIGHALARM: - // set alarm when bits with 1 in arg go high + /* Set alarm when bits with 1 in arg go high. */ priv->highalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; - if (priv->minor == GPIO_MINOR_A) { + if (priv->minor == GPIO_MINOR_A) gpio_pa_high_alarms |= arg; - } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_LOWALARM: - // set alarm when bits with 1 in arg go low + /* Set alarm when bits with 1 in arg go low. */ priv->lowalarm |= arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); gpio_some_alarms = 1; - if (priv->minor == GPIO_MINOR_A) { + if (priv->minor == GPIO_MINOR_A) gpio_pa_low_alarms |= arg; - } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_CLRALARM: - // clear alarm for bits with 1 in arg + /* Clear alarm for bits with 1 in arg. */ priv->highalarm &= ~arg; priv->lowalarm &= ~arg; - spin_lock(&alarm_lock); + spin_lock_irqsave(&alarm_lock, flags); if (priv->minor == GPIO_MINOR_A) { if (gpio_pa_high_alarms & arg || - gpio_pa_low_alarms & arg) { + gpio_pa_low_alarms & arg) /* Must update the gpio_pa_*alarms masks */ - } + ; } - spin_unlock(&alarm_lock); + spin_unlock_irqrestore(&alarm_lock, flags); break; case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ /* Read direction 0=input 1=output */ @@ -633,8 +654,7 @@ gpio_ioctl(struct inode *inode, struct file *file, if (!((priv->clk_mask & changeable_bits[priv->minor]) && (priv->data_mask & changeable_bits[priv->minor]) && (priv->clk_mask & dir_shadow) && - (priv->data_mask & dir_shadow))) - { + (priv->data_mask & dir_shadow))) { priv->clk_mask = 0; priv->data_mask = 0; return -EPERM; @@ -644,34 +664,34 @@ gpio_ioctl(struct inode *inode, struct file *file, case IO_READ_INBITS: /* *arg is result of reading the input pins */ val = *data_in[priv->minor]; - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; return 0; break; case IO_READ_OUTBITS: /* *arg is result of reading the output shadow */ val = *data_out[priv->minor]; - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; break; case IO_SETGET_INPUT: /* bits set in *arg is set to input, * *arg updated with current input pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) return -EFAULT; val = setget_input(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; break; case IO_SETGET_OUTPUT: /* bits set in *arg is set to output, * *arg updated with current output pins. */ - if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) return -EFAULT; val = setget_output(priv, val); - if (copy_to_user((unsigned long*)arg, &val, sizeof(val))) + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) return -EFAULT; break; default: @@ -684,6 +704,133 @@ gpio_ioctl(struct inode *inode, struct file *file, return 0; } +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static int +virtual_gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + unsigned short val; + unsigned short shadow; + struct gpio_private *priv = (struct gpio_private *)file->private_data; + + switch (_IOC_NR(cmd)) { + case IO_SETBITS: + local_irq_save(flags); + /* Set changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow |= (arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_CLRBITS: + local_irq_save(flags); + /* Clear changeable bits with a 1 in arg. */ + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + shadow |= ~*dir_oe[priv->minor]; + shadow &= ~(arg & changeable_bits[priv->minor]); + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + local_irq_restore(flags); + break; + case IO_HIGHALARM: + /* Set alarm when bits with 1 in arg go high. */ + priv->highalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_LOWALARM: + /* Set alarm when bits with 1 in arg go low. */ + priv->lowalarm |= arg; + spin_lock(&alarm_lock); + gpio_some_alarms = 1; + spin_unlock(&alarm_lock); + break; + case IO_CLRALARM: + /* Clear alarm for bits with 1 in arg. */ + priv->highalarm &= ~arg; + priv->lowalarm &= ~arg; + spin_lock(&alarm_lock); + spin_unlock(&alarm_lock); + break; + case IO_CFG_WRITE_MODE: + { + unsigned long dir_shadow; + dir_shadow = *dir_oe[priv->minor]; + + priv->clk_mask = arg & 0xFF; + priv->data_mask = (arg >> 8) & 0xFF; + priv->write_msb = (arg >> 16) & 0x01; + /* Check if we're allowed to change the bits and + * the direction is correct + */ + if (!((priv->clk_mask & changeable_bits[priv->minor]) && + (priv->data_mask & changeable_bits[priv->minor]) && + (priv->clk_mask & dir_shadow) && + (priv->data_mask & dir_shadow))) { + priv->clk_mask = 0; + priv->data_mask = 0; + return -EPERM; + } + break; + } + case IO_READ_INBITS: + /* *arg is result of reading the input pins */ + val = cached_virtual_gpio_read; + val &= ~*dir_oe[priv->minor]; + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + return 0; + break; + case IO_READ_OUTBITS: + /* *arg is result of reading the output shadow */ + i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val)); + val &= *dir_oe[priv->minor]; + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + break; + case IO_SETGET_INPUT: + { + /* bits set in *arg is set to input, + * *arg updated with current input pins. + */ + unsigned short input_mask = ~*dir_oe[priv->minor]; + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) + return -EFAULT; + val = setget_input(priv, val); + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + if ((input_mask & val) != input_mask) { + /* Input pins changed. All ports desired as input + * should be set to logic 1. + */ + unsigned short change = input_mask ^ val; + i2c_read(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + shadow &= ~change; + shadow |= val; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, + sizeof(shadow)); + } + break; + } + case IO_SETGET_OUTPUT: + /* bits set in *arg is set to output, + * *arg updated with current output pins. + */ + if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) + return -EFAULT; + val = setget_output(priv, val); + if (copy_to_user((unsigned long *)arg, &val, sizeof(val))) + return -EFAULT; + break; + default: + return -EINVAL; + } /* switch */ + return 0; +} +#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */ + static int gpio_leds_ioctl(unsigned int cmd, unsigned long arg) { @@ -694,8 +841,8 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) case IO_LEDACTIVE_SET: green = ((unsigned char) arg) & 1; red = (((unsigned char) arg) >> 1) & 1; - LED_ACTIVE_SET_G(green); - LED_ACTIVE_SET_R(red); + CRIS_LED_ACTIVE_SET_G(green); + CRIS_LED_ACTIVE_SET_R(red); break; default: @@ -705,7 +852,7 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) return 0; } -const struct file_operations gpio_fops = { +struct file_operations gpio_fops = { .owner = THIS_MODULE, .poll = gpio_poll, .ioctl = gpio_ioctl, @@ -714,6 +861,66 @@ const struct file_operations gpio_fops = { .release = gpio_release, }; +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO +static void +virtual_gpio_init(void) +{ + reg_gio_rw_intr_cfg intr_cfg; + reg_gio_rw_intr_mask intr_mask; + unsigned short shadow; + + shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */ + shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT; + i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow)); + + /* Set interrupt mask and on what state the interrupt shall trigger. + * For virtual gpio the interrupt shall trigger on logic '0'. + */ + intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg); + intr_mask = REG_RD(gio, regi_gio, rw_intr_mask); + + switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) { + case 0: + intr_cfg.pa0 = regk_gio_lo; + intr_mask.pa0 = regk_gio_yes; + break; + case 1: + intr_cfg.pa1 = regk_gio_lo; + intr_mask.pa1 = regk_gio_yes; + break; + case 2: + intr_cfg.pa2 = regk_gio_lo; + intr_mask.pa2 = regk_gio_yes; + break; + case 3: + intr_cfg.pa3 = regk_gio_lo; + intr_mask.pa3 = regk_gio_yes; + break; + case 4: + intr_cfg.pa4 = regk_gio_lo; + intr_mask.pa4 = regk_gio_yes; + break; + case 5: + intr_cfg.pa5 = regk_gio_lo; + intr_mask.pa5 = regk_gio_yes; + break; + case 6: + intr_cfg.pa6 = regk_gio_lo; + intr_mask.pa6 = regk_gio_yes; + break; + case 7: + intr_cfg.pa7 = regk_gio_lo; + intr_mask.pa7 = regk_gio_yes; + break; + } + + REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg); + REG_WR(gio, regi_gio, rw_intr_mask, intr_mask); + + gpio_pa_low_alarms |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN); + gpio_some_alarms = 1; +} +#endif /* main driver initialization routine, called from mem.c */ @@ -721,7 +928,6 @@ static __init int gpio_init(void) { int res; - reg_intr_vect_rw_mask intr_mask; /* do the formalities */ @@ -732,30 +938,30 @@ gpio_init(void) } /* Clear all leds */ - LED_NETWORK_SET(0); - LED_ACTIVE_SET(0); - LED_DISK_READ(0); - LED_DISK_WRITE(0); - - printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n"); + CRIS_LED_NETWORK_GRP0_SET(0); + CRIS_LED_NETWORK_GRP1_SET(0); + CRIS_LED_ACTIVE_SET(0); + CRIS_LED_DISK_READ(0); + CRIS_LED_DISK_WRITE(0); + + printk(KERN_INFO "ETRAX FS GPIO driver v2.5, (c) 2003-2007 " + "Axis Communications AB\n"); /* We call etrax_gpio_wake_up_check() from timer interrupt and * from cpu_idle() in kernel/process.c * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * in some tests. */ - if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio poll", &alarmlist)) { - printk("err: timer0 irq for gpio\n"); - } - if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt, - IRQF_SHARED | IRQF_DISABLED,"gpio PA", &alarmlist)) { - printk("err: PA irq for gpio\n"); - } - /* enable the gio and timer irq in global config */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.timer = 1; - intr_mask.gen_io = 1; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); + if (request_irq(TIMER0_INTR_VECT, gpio_poll_timer_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio poll", &alarmlist)) + printk(KERN_ERR "timer0 irq for gpio\n"); + + if (request_irq(GIO_INTR_VECT, gpio_pa_interrupt, + IRQF_SHARED | IRQF_DISABLED, "gpio PA", &alarmlist)) + printk(KERN_ERR "PA irq for gpio\n"); + +#ifdef CONFIG_ETRAX_VIRTUAL_GPIO + virtual_gpio_init(); +#endif return res; } diff --git a/arch/cris/arch-v32/drivers/nandflash.c b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c index 5ce015c6bb0..aa01b134458 100644 --- a/arch/cris/arch-v32/drivers/nandflash.c +++ b/arch/cris/arch-v32/drivers/mach-fs/nandflash.c @@ -4,9 +4,7 @@ * Copyright (c) 2004 * * Derived from drivers/mtd/nand/spia.c - * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) - * - * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $ + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,10 +19,10 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <asm/arch/memmap.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/gio_defs.h> -#include <asm/arch/hwregs/bif_core_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/gio_defs.h> +#include <hwregs/bif_core_defs.h> #include <asm/io.h> #define CE_BIT 4 @@ -32,44 +30,65 @@ #define ALE_BIT 6 #define BY_BIT 7 -static struct mtd_info *crisv32_mtd = NULL; +struct mtd_info_wrapper { + struct mtd_info info; + struct nand_chip chip; +}; + +/* Bitmask for control pins */ +#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT)) + +/* Bitmask for mtd nand control bits */ +#define CTRL_BITMASK (NAND_NCE | NAND_CLE | NAND_ALE) + + +static struct mtd_info *crisv32_mtd; /* * hardware specific access to control-lines -*/ -static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd) + */ +static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) { unsigned long flags; - reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout); + reg_gio_rw_pa_dout dout; + struct nand_chip *this = mtd->priv; local_irq_save(flags); - switch(cmd){ - case NAND_CTL_SETCLE: - dout.data |= (1<<CLE_BIT); - break; - case NAND_CTL_CLRCLE: - dout.data &= ~(1<<CLE_BIT); - break; - case NAND_CTL_SETALE: - dout.data |= (1<<ALE_BIT); - break; - case NAND_CTL_CLRALE: - dout.data &= ~(1<<ALE_BIT); - break; - case NAND_CTL_SETNCE: - dout.data |= (1<<CE_BIT); - break; - case NAND_CTL_CLRNCE: - dout.data &= ~(1<<CE_BIT); - break; + + /* control bits change */ + if (ctrl & NAND_CTRL_CHANGE) { + dout = REG_RD(gio, regi_gio, rw_pa_dout); + dout.data &= ~PIN_BITMASK; + +#if (CE_BIT == 4 && NAND_NCE == 1 && \ + CLE_BIT == 5 && NAND_CLE == 2 && \ + ALE_BIT == 6 && NAND_ALE == 4) + /* Pins in same order as control bits, but shifted. + * Optimize for this case; works for 2.6.18 */ + dout.data |= ((ctrl & CTRL_BITMASK) ^ NAND_NCE) << CE_BIT; +#else + /* the slow way */ + if (!(ctrl & NAND_NCE)) + dout.data |= (1 << CE_BIT); + if (ctrl & NAND_CLE) + dout.data |= (1 << CLE_BIT); + if (ctrl & NAND_ALE) + dout.data |= (1 << ALE_BIT); +#endif + REG_WR(gio, regi_gio, rw_pa_dout, dout); } - REG_WR(gio, regi_gio, rw_pa_dout, dout); + + /* command to chip */ + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); + local_irq_restore(flags); } /* * read device ready pin */ -int crisv32_device_ready(struct mtd_info *mtd) +static int crisv32_device_ready(struct mtd_info *mtd) { reg_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din); return ((din.data & (1 << BY_BIT)) >> BY_BIT); @@ -78,21 +97,23 @@ int crisv32_device_ready(struct mtd_info *mtd) /* * Main initialization routine */ -struct mtd_info* __init crisv32_nand_flash_probe (void) +struct mtd_info *__init crisv32_nand_flash_probe(void) { void __iomem *read_cs; void __iomem *write_cs; - reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg); + reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, + rw_grp3_cfg); reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe); + struct mtd_info_wrapper *wrapper; struct nand_chip *this; int err = 0; /* Allocate memory for MTD device structure and private data */ - crisv32_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), - GFP_KERNEL); - if (!crisv32_mtd) { - printk ("Unable to allocate CRISv32 NAND MTD device structure.\n"); + wrapper = kzalloc(sizeof(struct mtd_info_wrapper), GFP_KERNEL); + if (!wrapper) { + printk(KERN_ERR "Unable to allocate CRISv32 NAND MTD " + "device structure.\n"); err = -ENOMEM; return NULL; } @@ -101,45 +122,42 @@ struct mtd_info* __init crisv32_nand_flash_probe (void) write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192); if (!read_cs || !write_cs) { - printk("CRISv32 NAND ioremap failed\n"); + printk(KERN_ERR "CRISv32 NAND ioremap failed\n"); err = -EIO; goto out_mtd; } /* Get pointer to private data */ - this = (struct nand_chip *) (&crisv32_mtd[1]); + this = &wrapper->chip; + crisv32_mtd = &wrapper->info; pa_oe.oe |= 1 << CE_BIT; pa_oe.oe |= 1 << ALE_BIT; pa_oe.oe |= 1 << CLE_BIT; - pa_oe.oe &= ~ (1 << BY_BIT); + pa_oe.oe &= ~(1 << BY_BIT); REG_WR(gio, regi_gio, rw_pa_oe, pa_oe); bif_cfg.gated_csp0 = regk_bif_core_rd; bif_cfg.gated_csp1 = regk_bif_core_wr; REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg); - /* Initialize structures */ - memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info)); - memset((char *) this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ crisv32_mtd->priv = this; /* Set address of NAND IO lines */ this->IO_ADDR_R = read_cs; this->IO_ADDR_W = write_cs; - this->hwcontrol = crisv32_hwcontrol; + this->cmd_ctrl = crisv32_hwcontrol; this->dev_ready = crisv32_device_ready; /* 20 us command delay time */ this->chip_delay = 20; - this->eccmode = NAND_ECC_SOFT; + this->ecc.mode = NAND_ECC_SOFT; /* Enable the following for a flash based bad block table */ - this->options = NAND_USE_FLASH_BBT; + /* this->options = NAND_USE_FLASH_BBT; */ - /* Scan to find existence of the device */ - if (nand_scan (crisv32_mtd, 1)) { + /* Scan to find existance of the device */ + if (nand_scan(crisv32_mtd, 1)) { err = -ENXIO; goto out_ior; } @@ -150,7 +168,7 @@ out_ior: iounmap((void *)read_cs); iounmap((void *)write_cs); out_mtd: - kfree (crisv32_mtd); - return NULL; + kfree(wrapper); + return NULL; } diff --git a/arch/cris/arch-v32/drivers/pcf8563.c b/arch/cris/arch-v32/drivers/pcf8563.c index 6dbd700d3d6..53db3870ba0 100644 --- a/arch/cris/arch-v32/drivers/pcf8563.c +++ b/arch/cris/arch-v32/drivers/pcf8563.c @@ -10,7 +10,7 @@ * 400 kbits/s. The built-in word address register is incremented * automatically after each written or read byte. * - * Copyright (c) 2002-2003, Axis Communications AB + * Copyright (c) 2002-2007, Axis Communications AB * All rights reserved. * * Author: Tobias Anderberg <tobiasa@axis.com>. @@ -26,6 +26,7 @@ #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/bcd.h> +#include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -37,24 +38,27 @@ #define PCF8563_MAJOR 121 /* Local major number. */ #define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ #define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.1 $" +#define DRIVER_VERSION "$Revision: 1.17 $" /* Two simple wrapper macros, saves a few keystrokes. */ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) +static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ + static const unsigned char days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long); -int pcf8563_open(struct inode *, struct file *); -int pcf8563_release(struct inode *, struct file *); + +/* Cache VL bit value read at driver init since writing the RTC_SECOND + * register clears the VL status. + */ +static int voltage_low; static const struct file_operations pcf8563_fops = { .owner = THIS_MODULE, - .ioctl = pcf8563_ioctl, - .open = pcf8563_open, - .release = pcf8563_release, + .ioctl = pcf8563_ioctl }; unsigned char @@ -62,7 +66,7 @@ pcf8563_readreg(int reg) { unsigned char res = rtc_read(reg); - /* The PCF8563 does not return 0 for unimplemented bits */ + /* The PCF8563 does not return 0 for unimplemented bits. */ switch (reg) { case RTC_SECONDS: case RTC_MINUTES: @@ -95,11 +99,6 @@ pcf8563_readreg(int reg) void pcf8563_writereg(int reg, unsigned char val) { -#ifdef CONFIG_ETRAX_RTC_READONLY - if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR)) - return; -#endif - rtc_write(reg, val); } @@ -114,11 +113,13 @@ get_rtc_time(struct rtc_time *tm) tm->tm_mon = rtc_read(RTC_MONTH); tm->tm_year = rtc_read(RTC_YEAR); - if (tm->tm_sec & 0x80) - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + if (tm->tm_sec & 0x80) { + printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " "information is no longer guaranteed!\n", PCF8563_NAME); + } - tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0); + tm->tm_year = BCD_TO_BIN(tm->tm_year) + + ((tm->tm_mon & 0x80) ? 100 : 0); tm->tm_sec &= 0x7F; tm->tm_min &= 0x7F; tm->tm_hour &= 0x3F; @@ -137,8 +138,19 @@ get_rtc_time(struct rtc_time *tm) int __init pcf8563_init(void) { + static int res; + static int first = 1; + + if (!first) + return res; + first = 0; + /* Initiate the i2c protocol. */ - i2c_init(); + res = i2c_init(); + if (res < 0) { + printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); + return res; + } /* * First of all we need to reset the chip. This is done by @@ -170,24 +182,20 @@ pcf8563_init(void) if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) goto err; - if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { - printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", - PCF8563_NAME, PCF8563_MAJOR); - return -1; + /* Check for low voltage, and warn about it. */ + if (rtc_read(RTC_SECONDS) & 0x80) { + voltage_low = 1; + printk(KERN_WARNING "%s: RTC Voltage Low - reliable " + "date/time information is no longer guaranteed!\n", + PCF8563_NAME); } - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - - /* Check for low voltage, and warn about it.. */ - if (rtc_read(RTC_SECONDS) & 0x80) - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " - "information is no longer guaranteed!\n", PCF8563_NAME); - - return 0; + return res; err: printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - return -1; + res = -1; + return res; } void __exit @@ -200,8 +208,8 @@ pcf8563_exit(void) * ioctl calls for this driver. Why return -ENOTTY upon error? Because * POSIX says so! */ -int -pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +int pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) { /* Some sanity checks. */ if (_IOC_TYPE(cmd) != RTC_MAGIC) @@ -211,125 +219,147 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned return -ENOTTY; switch (cmd) { - case RTC_RD_TIME: - { - struct rtc_time tm; - - memset(&tm, 0, sizeof (struct rtc_time)); - get_rtc_time(&tm); - - if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) { - return -EFAULT; - } - - return 0; + case RTC_RD_TIME: + { + struct rtc_time tm; + + mutex_lock(&rtc_lock); + memset(&tm, 0, sizeof tm); + get_rtc_time(&tm); + + if (copy_to_user((struct rtc_time *) arg, &tm, + sizeof tm)) { + spin_unlock(&rtc_lock); + return -EFAULT; } - case RTC_SET_TIME: - { -#ifdef CONFIG_ETRAX_RTC_READONLY + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_SET_TIME: + { + int leap; + int year; + int century; + struct rtc_time tm; + + memset(&tm, 0, sizeof tm); + if (!capable(CAP_SYS_TIME)) return -EPERM; -#else - int leap; - int year; - int century; - struct rtc_time tm; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - /* - * Check if tm.tm_year is a leap year. A year is a leap - * year if it is divisible by 4 but not 100, except - * that years divisible by 400 _are_ leap years. - */ - year = tm.tm_year; - leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_wday >= 7) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - BIN_TO_BCD(tm.tm_year); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_sec); - tm.tm_mon |= century; - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - - return 0; -#endif /* !CONFIG_ETRAX_RTC_READONLY */ - } - case RTC_VLOW_RD: - { - int vl_bit = 0; + if (copy_from_user(&tm, (struct rtc_time *) arg, + sizeof tm)) + return -EFAULT; + + /* Convert from struct tm to struct rtc_time. */ + tm.tm_year += 1900; + tm.tm_mon += 1; + + /* + * Check if tm.tm_year is a leap year. A year is a leap + * year if it is divisible by 4 but not 100, except + * that years divisible by 400 _are_ leap years. + */ + year = tm.tm_year; + leap = (tm.tm_mon == 2) && + ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); + + /* Perform some sanity checks. */ + if ((tm.tm_year < 1970) || + (tm.tm_mon > 12) || + (tm.tm_mday == 0) || + (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || + (tm.tm_wday >= 7) || + (tm.tm_hour >= 24) || + (tm.tm_min >= 60) || + (tm.tm_sec >= 60)) + return -EINVAL; + + century = (tm.tm_year >= 2000) ? 0x80 : 0; + tm.tm_year = tm.tm_year % 100; + + BIN_TO_BCD(tm.tm_year); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_sec); + tm.tm_mon |= century; + + mutex_lock(&rtc_lock); + + rtc_write(RTC_YEAR, tm.tm_year); + rtc_write(RTC_MONTH, tm.tm_mon); + rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ + rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); + rtc_write(RTC_HOURS, tm.tm_hour); + rtc_write(RTC_MINUTES, tm.tm_min); + rtc_write(RTC_SECONDS, tm.tm_sec); + + mutex_unlock(&rtc_lock); + + return 0; + } + case RTC_VL_READ: + if (voltage_low) + printk(KERN_ERR "%s: RTC Voltage Low - " + "reliable date/time information is no " + "longer guaranteed!\n", PCF8563_NAME); - if (rtc_read(RTC_SECONDS) & 0x80) { - vl_bit = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - if (copy_to_user((int *) arg, &vl_bit, sizeof(int))) - return -EFAULT; + if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) + return -EFAULT; + return 0; - return 0; - } + case RTC_VL_CLR: + { + /* Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. */ - case RTC_VLOW_SET: - { - /* Clear the VL bit in the seconds register */ - int ret = rtc_read(RTC_SECONDS); + int ret = rtc_read(RTC_SECONDS); - rtc_write(RTC_SECONDS, (ret & 0x7F)); + rtc_write(RTC_SECONDS, (ret & 0x7F)); - return 0; - } + /* Clear the cached value. */ + voltage_low = 0; - default: - return -ENOTTY; + return 0; + } + default: + return -ENOTTY; } return 0; } -int -pcf8563_open(struct inode *inode, struct file *filp) +static int __init pcf8563_register(void) { - return 0; -} + if (pcf8563_init() < 0) { + printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " + "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); + return -1; + } + + if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { + printk(KERN_INFO "%s: Unable to get major numer %d for RTC " + "device.\n", PCF8563_NAME, PCF8563_MAJOR); + return -1; + } + + printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, + DRIVER_VERSION); + + /* Check for low voltage, and warn about it. */ + if (voltage_low) { + printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " + "information is no longer guaranteed!\n", PCF8563_NAME); + } -int -pcf8563_release(struct inode *inode, struct file *filp) -{ return 0; } -module_init(pcf8563_init); +module_init(pcf8563_register); module_exit(pcf8563_exit); diff --git a/arch/cris/arch-v32/drivers/sync_serial.c b/arch/cris/arch-v32/drivers/sync_serial.c index d581b0a92a3..47c377df6fb 100644 --- a/arch/cris/arch-v32/drivers/sync_serial.c +++ b/arch/cris/arch-v32/drivers/sync_serial.c @@ -1,5 +1,5 @@ /* - * Simple synchronous serial port driver for ETRAX FS. + * Simple synchronous serial port driver for ETRAX FS and Artpec-3. * * Copyright (c) 2005 Axis Communications AB * @@ -21,17 +21,18 @@ #include <linux/spinlock.h> #include <asm/io.h> -#include <asm/arch/dma.h> -#include <asm/arch/pinmux.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/sser_defs.h> -#include <asm/arch/hwregs/dma_defs.h> -#include <asm/arch/hwregs/dma.h> -#include <asm/arch/hwregs/intr_vect_defs.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/reg_map.h> +#include <dma.h> +#include <pinmux.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/sser_defs.h> +#include <hwregs/dma_defs.h> +#include <hwregs/dma.h> +#include <hwregs/intr_vect_defs.h> +#include <hwregs/intr_vect.h> +#include <hwregs/reg_map.h> #include <asm/sync_serial.h> + /* The receiver is a bit tricky beacuse of the continuous stream of data.*/ /* */ /* Three DMA descriptors are linked together. Each DMA descriptor is */ @@ -63,8 +64,10 @@ /* words can be handled */ #define IN_BUFFER_SIZE 12288 #define IN_DESCR_SIZE 256 -#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) -#define OUT_BUFFER_SIZE 4096 +#define NBR_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE) + +#define OUT_BUFFER_SIZE 1024*8 +#define NBR_OUT_DESCR 8 #define DEFAULT_FRAME_RATE 0 #define DEFAULT_WORD_RATE 7 @@ -78,6 +81,8 @@ #define DEBUGPOLL(x) #define DEBUGRXINT(x) #define DEBUGTXINT(x) +#define DEBUGTRDMA(x) +#define DEBUGOUTBUF(x) typedef struct sync_port { @@ -97,10 +102,11 @@ typedef struct sync_port int output; int input; - volatile unsigned int out_count; /* Remaining bytes for current transfer */ - unsigned char* outp; /* Current position in out_buffer */ - volatile unsigned char* volatile readp; /* Next byte to be read by application */ - volatile unsigned char* volatile writep; /* Next byte to be written by etrax */ + /* Next byte to be read by application */ + volatile unsigned char *volatile readp; + /* Next byte to be written by etrax */ + volatile unsigned char *volatile writep; + unsigned int in_buffer_size; unsigned int inbufchunk; unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32))); @@ -108,11 +114,30 @@ typedef struct sync_port unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32))); struct dma_descr_data* next_rx_desc; struct dma_descr_data* prev_rx_desc; + + /* Pointer to the first available descriptor in the ring, + * unless active_tr_descr == catch_tr_descr and a dma + * transfer is active */ + struct dma_descr_data *active_tr_descr; + + /* Pointer to the first allocated descriptor in the ring */ + struct dma_descr_data *catch_tr_descr; + + /* Pointer to the descriptor with the current end-of-list */ + struct dma_descr_data *prev_tr_descr; int full; - dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16))); + /* Pointer to the first byte being read by DMA + * or current position in out_buffer if not using DMA. */ + unsigned char *out_rd_ptr; + + /* Number of bytes currently locked for being read by DMA */ + int out_buf_count; + + dma_descr_data in_descr[NBR_IN_DESCR] __attribute__ ((__aligned__(16))); dma_descr_context in_context __attribute__ ((__aligned__(32))); - dma_descr_data out_descr __attribute__ ((__aligned__(16))); + dma_descr_data out_descr[NBR_OUT_DESCR] + __attribute__ ((__aligned__(16))); dma_descr_context out_context __attribute__ ((__aligned__(32))); wait_queue_head_t out_wait_q; wait_queue_head_t in_wait_q; @@ -143,11 +168,11 @@ static ssize_t sync_serial_read(struct file *file, char *buf, #endif static void send_word(sync_port* port); -static void start_dma(struct sync_port *port, const char* data, int count); +static void start_dma_out(struct sync_port *port, const char *data, int count); static void start_dma_in(sync_port* port); #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs); -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t tr_interrupt(int irq, void *dev_id); +static irqreturn_t rx_interrupt(int irq, void *dev_id); #endif #if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \ @@ -157,22 +182,49 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs); #define SYNC_SER_MANUAL #endif #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static irqreturn_t manual_interrupt(int irq, void *dev_id); +#endif + +#ifdef CONFIG_ETRAXFS /* ETRAX FS */ +#define OUT_DMA_NBR 4 +#define IN_DMA_NBR 5 +#define PINMUX_SSER pinmux_sser0 +#define SYNCSER_INST regi_sser0 +#define SYNCSER_INTR_VECT SSER0_INTR_VECT +#define OUT_DMA_INST regi_dma4 +#define IN_DMA_INST regi_dma5 +#define DMA_OUT_INTR_VECT DMA4_INTR_VECT +#define DMA_IN_INTR_VECT DMA5_INTR_VECT +#define REQ_DMA_SYNCSER dma_sser0 +#else /* Artpec-3 */ +#define OUT_DMA_NBR 6 +#define IN_DMA_NBR 7 +#define PINMUX_SSER pinmux_sser +#define SYNCSER_INST regi_sser +#define SYNCSER_INTR_VECT SSER_INTR_VECT +#define OUT_DMA_INST regi_dma6 +#define IN_DMA_INST regi_dma7 +#define DMA_OUT_INTR_VECT DMA6_INTR_VECT +#define DMA_IN_INTR_VECT DMA7_INTR_VECT +#define REQ_DMA_SYNCSER dma_sser #endif /* The ports */ static struct sync_port ports[]= { { - .regi_sser = regi_sser0, - .regi_dmaout = regi_dma4, - .regi_dmain = regi_dma5, + .regi_sser = SYNCSER_INST, + .regi_dmaout = OUT_DMA_INST, + .regi_dmain = IN_DMA_INST, #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA) .use_dma = 1, #else .use_dma = 0, #endif - }, + } +#ifdef CONFIG_ETRAXFS + , + { .regi_sser = regi_sser1, .regi_dmaout = regi_dma6, @@ -183,9 +235,10 @@ static struct sync_port ports[]= .use_dma = 0, #endif } +#endif }; -#define NUMBER_OF_PORTS ARRAY_SIZE(ports) +#define NBR_PORTS ARRAY_SIZE(ports) static const struct file_operations sync_serial_fops = { .owner = THIS_MODULE, @@ -200,19 +253,21 @@ static const struct file_operations sync_serial_fops = { static int __init etrax_sync_serial_init(void) { ports[0].enabled = 0; +#ifdef CONFIG_ETRAXFS ports[1].enabled = 0; - - if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) - { - printk("unable to get major for synchronous serial port\n"); +#endif + if (register_chrdev(SYNC_SERIAL_MAJOR, "sync serial", + &sync_serial_fops) < 0) { + printk(KERN_WARNING + "Unable to get major for synchronous serial port\n"); return -EBUSY; } /* Initialize Ports */ #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) - if (crisv32_pinmux_alloc_fixed(pinmux_sser0)) - { - printk("Unable to allocate pins for syncrhronous serial port 0\n"); + if (crisv32_pinmux_alloc_fixed(PINMUX_SSER)) { + printk(KERN_WARNING + "Unable to alloc pins for synchronous serial port 0\n"); return -EIO; } ports[0].enabled = 1; @@ -220,33 +275,40 @@ static int __init etrax_sync_serial_init(void) #endif #if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) - if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) - { - printk("Unable to allocate pins for syncrhronous serial port 0\n"); + if (crisv32_pinmux_alloc_fixed(pinmux_sser1)) { + printk(KERN_WARNING + "Unable to alloc pins for synchronous serial port 0\n"); return -EIO; } ports[1].enabled = 1; initialize_port(1); #endif - printk("ETRAX FS synchronous serial port driver\n"); +#ifdef CONFIG_ETRAXFS + printk(KERN_INFO "ETRAX FS synchronous serial port driver\n"); +#else + printk(KERN_INFO "Artpec-3 synchronous serial port driver\n"); +#endif return 0; } static void __init initialize_port(int portnbr) { - struct sync_port* port = &ports[portnbr]; + int __attribute__((unused)) i; + struct sync_port *port = &ports[portnbr]; reg_sser_rw_cfg cfg = {0}; reg_sser_rw_frm_cfg frm_cfg = {0}; reg_sser_rw_tr_cfg tr_cfg = {0}; reg_sser_rw_rec_cfg rec_cfg = {0}; - DEBUG(printk("Init sync serial port %d\n", portnbr)); + DEBUG(printk(KERN_DEBUG "Init sync serial port %d\n", portnbr)); port->port_nbr = portnbr; port->init_irqs = 1; - port->outp = port->out_buffer; + port->out_rd_ptr = port->out_buffer; + port->out_buf_count = 0; + port->output = 1; port->input = 0; @@ -255,7 +317,7 @@ static void __init initialize_port(int portnbr) port->in_buffer_size = IN_BUFFER_SIZE; port->inbufchunk = IN_DESCR_SIZE; port->next_rx_desc = &port->in_descr[0]; - port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1]; + port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR-1]; port->prev_rx_desc->eol = 1; init_waitqueue_head(&port->out_wait_q); @@ -286,8 +348,13 @@ static void __init initialize_port(int portnbr) tr_cfg.sample_size = 7; tr_cfg.sh_dir = regk_sser_msbfirst; tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; +#if 0 tr_cfg.rate_ctrl = regk_sser_bulk; tr_cfg.data_pin_use = regk_sser_dout; +#else + tr_cfg.rate_ctrl = regk_sser_iso; + tr_cfg.data_pin_use = regk_sser_dout; +#endif tr_cfg.bulk_wspace = 1; REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); @@ -296,6 +363,27 @@ static void __init initialize_port(int portnbr) rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no; rec_cfg.fifo_thr = regk_sser_inf; REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + +#ifdef SYNC_SER_DMA + /* Setup the descriptor ring for dma out/transmit. */ + for (i = 0; i < NBR_OUT_DESCR; i++) { + port->out_descr[i].wait = 0; + port->out_descr[i].intr = 1; + port->out_descr[i].eol = 0; + port->out_descr[i].out_eop = 0; + port->out_descr[i].next = + (dma_descr_data *)virt_to_phys(&port->out_descr[i+1]); + } + + /* Create a ring from the list. */ + port->out_descr[NBR_OUT_DESCR-1].next = + (dma_descr_data *)virt_to_phys(&port->out_descr[0]); + + /* Setup context for traversing the ring. */ + port->active_tr_descr = &port->out_descr[0]; + port->prev_tr_descr = &port->out_descr[NBR_OUT_DESCR-1]; + port->catch_tr_descr = &port->out_descr[0]; +#endif } static inline int sync_data_avail(struct sync_port *port) @@ -311,7 +399,7 @@ static inline int sync_data_avail(struct sync_port *port) * ^rp ^wp ^wp ^rp */ - if (end >= start) + if (end >= start) avail = end - start; else avail = port->in_buffer_size - (start - end); @@ -331,7 +419,7 @@ static inline int sync_data_avail_to_end(struct sync_port *port) * ^rp ^wp ^wp ^rp */ - if (end >= start) + if (end >= start) avail = end - start; else avail = port->flip + port->in_buffer_size - start; @@ -341,66 +429,69 @@ static inline int sync_data_avail_to_end(struct sync_port *port) static int sync_serial_open(struct inode *inode, struct file *file) { int dev = iminor(inode); - sync_port* port; + sync_port *port; reg_dma_rw_cfg cfg = {.en = regk_dma_yes}; reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes}; - DEBUG(printk("Open sync serial port %d\n", dev)); + DEBUG(printk(KERN_DEBUG "Open sync serial port %d\n", dev)); - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { - DEBUG(printk("Invalid minor %d\n", dev)); + DEBUG(printk(KERN_DEBUG "Invalid minor %d\n", dev)); return -ENODEV; } port = &ports[dev]; /* Allow open this device twice (assuming one reader and one writer) */ if (port->busy == 2) { - DEBUG(printk("Device is busy.. \n")); + DEBUG(printk(KERN_DEBUG "Device is busy.. \n")); return -EBUSY; } + + if (port->init_irqs) { if (port->use_dma) { - if (port == &ports[0]){ + if (port == &ports[0]) { #ifdef SYNC_SER_DMA - if(request_irq(DMA4_INTR_VECT, - tr_interrupt, - 0, - "synchronous serial 0 dma tr", - &ports[0])) { + if (request_irq(DMA_OUT_INTR_VECT, + tr_interrupt, + 0, + "synchronous serial 0 dma tr", + &ports[0])) { printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; - } else if(request_irq(DMA5_INTR_VECT, - rx_interrupt, - 0, - "synchronous serial 1 dma rx", - &ports[0])) { - free_irq(DMA4_INTR_VECT, &port[0]); + } else if (request_irq(DMA_IN_INTR_VECT, + rx_interrupt, + 0, + "synchronous serial 1 dma rx", + &ports[0])) { + free_irq(DMA_OUT_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 IRQ"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER0_TX_DMA_NBR, - "synchronous serial 0 dma tr", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser0)) { - free_irq(DMA4_INTR_VECT, &port[0]); - free_irq(DMA5_INTR_VECT, &port[0]); + } else if (crisv32_request_dma(OUT_DMA_NBR, + "synchronous serial 0 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + REQ_DMA_SYNCSER)) { + free_irq(DMA_OUT_INTR_VECT, &port[0]); + free_irq(DMA_IN_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 0 TX DMA channel"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER0_RX_DMA_NBR, - "synchronous serial 0 dma rec", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser0)) { - crisv32_free_dma(SYNC_SER0_TX_DMA_NBR); - free_irq(DMA4_INTR_VECT, &port[0]); - free_irq(DMA5_INTR_VECT, &port[0]); + } else if (crisv32_request_dma(IN_DMA_NBR, + "synchronous serial 0 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + REQ_DMA_SYNCSER)) { + crisv32_free_dma(OUT_DMA_NBR); + free_irq(DMA_OUT_INTR_VECT, &port[0]); + free_irq(DMA_IN_INTR_VECT, &port[0]); printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel"); return -EBUSY; } #endif } - else if (port == &ports[1]){ +#ifdef CONFIG_ETRAXFS + else if (port == &ports[1]) { #ifdef SYNC_SER_DMA if (request_irq(DMA6_INTR_VECT, tr_interrupt, @@ -417,20 +508,22 @@ static int sync_serial_open(struct inode *inode, struct file *file) free_irq(DMA6_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER1_TX_DMA_NBR, - "synchronous serial 1 dma tr", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser1)) { - free_irq(21, &ports[1]); - free_irq(20, &ports[1]); + } else if (crisv32_request_dma( + SYNC_SER1_TX_DMA_NBR, + "synchronous serial 1 dma tr", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { + free_irq(DMA6_INTR_VECT, &ports[1]); + free_irq(DMA7_INTR_VECT, &ports[1]); printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel"); return -EBUSY; - } else if (crisv32_request_dma(SYNC_SER1_RX_DMA_NBR, - "synchronous serial 3 dma rec", - DMA_VERBOSE_ON_ERROR, - 0, - dma_sser1)) { + } else if (crisv32_request_dma( + SYNC_SER1_RX_DMA_NBR, + "synchronous serial 3 dma rec", + DMA_VERBOSE_ON_ERROR, + 0, + dma_sser1)) { crisv32_free_dma(SYNC_SER1_TX_DMA_NBR); free_irq(DMA6_INTR_VECT, &ports[1]); free_irq(DMA7_INTR_VECT, &ports[1]); @@ -439,14 +532,14 @@ static int sync_serial_open(struct inode *inode, struct file *file) } #endif } - +#endif /* Enable DMAs */ REG_WR(dma, port->regi_dmain, rw_cfg, cfg); REG_WR(dma, port->regi_dmaout, rw_cfg, cfg); /* Enable DMA IRQs */ REG_WR(dma, port->regi_dmain, rw_intr_mask, intr_mask); REG_WR(dma, port->regi_dmaout, rw_intr_mask, intr_mask); - /* Set up wordsize = 2 for DMAs. */ + /* Set up wordsize = 1 for DMAs. */ DMA_WR_CMD (port->regi_dmain, regk_dma_set_w_size1); DMA_WR_CMD (port->regi_dmaout, regk_dma_set_w_size1); @@ -455,7 +548,7 @@ static int sync_serial_open(struct inode *inode, struct file *file) } else { /* !port->use_dma */ #ifdef SYNC_SER_MANUAL if (port == &ports[0]) { - if (request_irq(SSER0_INTR_VECT, + if (request_irq(SYNCSER_INTR_VECT, manual_interrupt, 0, "synchronous serial manual irq", @@ -463,7 +556,9 @@ static int sync_serial_open(struct inode *inode, struct file *file) printk("Can't allocate sync serial manual irq"); return -EBUSY; } - } else if (port == &ports[1]) { + } +#ifdef CONFIG_ETRAXFS + else if (port == &ports[1]) { if (request_irq(SSER1_INTR_VECT, manual_interrupt, 0, @@ -473,11 +568,13 @@ static int sync_serial_open(struct inode *inode, struct file *file) return -EBUSY; } } +#endif port->init_irqs = 0; #else panic("sync_serial: Manual mode not supported.\n"); #endif /* SYNC_SER_MANUAL */ } + } /* port->init_irqs */ port->busy++; @@ -487,9 +584,9 @@ static int sync_serial_open(struct inode *inode, struct file *file) static int sync_serial_release(struct inode *inode, struct file *file) { int dev = iminor(inode); - sync_port* port; + sync_port *port; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; @@ -506,17 +603,37 @@ static unsigned int sync_serial_poll(struct file *file, poll_table *wait) { int dev = iminor(file->f_path.dentry->d_inode); unsigned int mask = 0; - sync_port* port; + sync_port *port; DEBUGPOLL( static unsigned int prev_mask = 0; ); port = &ports[dev]; + + if (!port->started) { + reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); + reg_sser_rw_rec_cfg rec_cfg = + REG_RD(sser, port->regi_sser, rw_rec_cfg); + cfg.en = regk_sser_yes; + rec_cfg.rec_en = port->input; + REG_WR(sser, port->regi_sser, rw_cfg, cfg); + REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); + port->started = 1; + } + poll_wait(file, &port->out_wait_q, wait); poll_wait(file, &port->in_wait_q, wait); - /* Some room to write */ - if (port->out_count < OUT_BUFFER_SIZE) + + /* No active transfer, descriptors are available */ + if (port->output && !port->tr_running) + mask |= POLLOUT | POLLWRNORM; + + /* Descriptor and buffer space available. */ + if (port->output && + port->active_tr_descr != port->catch_tr_descr && + port->out_buf_count < OUT_BUFFER_SIZE) mask |= POLLOUT | POLLWRNORM; + /* At least an inbufchunk of data */ - if (sync_data_avail(port) >= port->inbufchunk) + if (port->input && sync_data_avail(port) >= port->inbufchunk) mask |= POLLIN | POLLRDNORM; DEBUGPOLL(if (mask != prev_mask) @@ -531,15 +648,16 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int return_val = 0; + int dma_w_size = regk_dma_set_w_size1; int dev = iminor(file->f_path.dentry->d_inode); - sync_port* port; + sync_port *port; reg_sser_rw_tr_cfg tr_cfg; reg_sser_rw_rec_cfg rec_cfg; reg_sser_rw_frm_cfg frm_cfg; reg_sser_rw_cfg gen_cfg; reg_sser_rw_intr_mask intr_mask; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -1; @@ -558,61 +676,81 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, case SSP_SPEED: if (GET_SPEED(arg) == CODEC) { + unsigned int freq; + gen_cfg.base_freq = regk_sser_f32; - /* FREQ = 0 => 4 MHz => clk_div = 7*/ - gen_cfg.clk_div = 6 + (1 << GET_FREQ(arg)); - } - else - { + + /* Clock divider will internally be + * gen_cfg.clk_div + 1. + */ + + freq = GET_FREQ(arg); + switch (freq) { + case FREQ_32kHz: + case FREQ_64kHz: + case FREQ_128kHz: + case FREQ_256kHz: + gen_cfg.clk_div = 125 * + (1 << (freq - FREQ_256kHz)) - 1; + break; + case FREQ_512kHz: + gen_cfg.clk_div = 62; + break; + case FREQ_1MHz: + case FREQ_2MHz: + case FREQ_4MHz: + gen_cfg.clk_div = 8 * (1 << freq) - 1; + break; + } + } else { gen_cfg.base_freq = regk_sser_f29_493; - switch (GET_SPEED(arg)) - { - case SSP150: - gen_cfg.clk_div = 29493000 / (150 * 8) - 1; - break; - case SSP300: - gen_cfg.clk_div = 29493000 / (300 * 8) - 1; - break; - case SSP600: - gen_cfg.clk_div = 29493000 / (600 * 8) - 1; - break; - case SSP1200: - gen_cfg.clk_div = 29493000 / (1200 * 8) - 1; - break; - case SSP2400: - gen_cfg.clk_div = 29493000 / (2400 * 8) - 1; - break; - case SSP4800: - gen_cfg.clk_div = 29493000 / (4800 * 8) - 1; - break; - case SSP9600: - gen_cfg.clk_div = 29493000 / (9600 * 8) - 1; - break; - case SSP19200: - gen_cfg.clk_div = 29493000 / (19200 * 8) - 1; - break; - case SSP28800: - gen_cfg.clk_div = 29493000 / (28800 * 8) - 1; - break; - case SSP57600: - gen_cfg.clk_div = 29493000 / (57600 * 8) - 1; - break; - case SSP115200: - gen_cfg.clk_div = 29493000 / (115200 * 8) - 1; - break; - case SSP230400: - gen_cfg.clk_div = 29493000 / (230400 * 8) - 1; - break; - case SSP460800: - gen_cfg.clk_div = 29493000 / (460800 * 8) - 1; - break; - case SSP921600: - gen_cfg.clk_div = 29493000 / (921600 * 8) - 1; - break; - case SSP3125000: - gen_cfg.base_freq = regk_sser_f100; - gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1; - break; + switch (GET_SPEED(arg)) { + case SSP150: + gen_cfg.clk_div = 29493000 / (150 * 8) - 1; + break; + case SSP300: + gen_cfg.clk_div = 29493000 / (300 * 8) - 1; + break; + case SSP600: + gen_cfg.clk_div = 29493000 / (600 * 8) - 1; + break; + case SSP1200: + gen_cfg.clk_div = 29493000 / (1200 * 8) - 1; + break; + case SSP2400: + gen_cfg.clk_div = 29493000 / (2400 * 8) - 1; + break; + case SSP4800: + gen_cfg.clk_div = 29493000 / (4800 * 8) - 1; + break; + case SSP9600: + gen_cfg.clk_div = 29493000 / (9600 * 8) - 1; + break; + case SSP19200: + gen_cfg.clk_div = 29493000 / (19200 * 8) - 1; + break; + case SSP28800: + gen_cfg.clk_div = 29493000 / (28800 * 8) - 1; + break; + case SSP57600: + gen_cfg.clk_div = 29493000 / (57600 * 8) - 1; + break; + case SSP115200: + gen_cfg.clk_div = 29493000 / (115200 * 8) - 1; + break; + case SSP230400: + gen_cfg.clk_div = 29493000 / (230400 * 8) - 1; + break; + case SSP460800: + gen_cfg.clk_div = 29493000 / (460800 * 8) - 1; + break; + case SSP921600: + gen_cfg.clk_div = 29493000 / (921600 * 8) - 1; + break; + case SSP3125000: + gen_cfg.base_freq = regk_sser_f100; + gen_cfg.clk_div = 100000000 / (3125000 * 8) - 1; + break; } } @@ -625,46 +763,60 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, case MASTER_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.out_on = regk_sser_tr; + frm_cfg.frame_pin_dir = regk_sser_out; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_OUTPUT: port->output = 1; port->input = 0; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_INPUT: port->output = 0; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; case MASTER_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_out; + frm_cfg.out_on = regk_sser_intern_tb; gen_cfg.clk_dir = regk_sser_out; break; case SLAVE_BIDIR: port->output = 1; port->input = 1; + frm_cfg.frame_pin_dir = regk_sser_in; gen_cfg.clk_dir = regk_sser_in; break; default: spin_unlock_irq(&port->lock); return -EINVAL; - } if (!port->use_dma || (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT)) intr_mask.rdav = regk_sser_yes; break; case SSP_FRAME_SYNC: - if (arg & NORMAL_SYNC) + if (arg & NORMAL_SYNC) { + frm_cfg.rec_delay = 1; frm_cfg.tr_delay = 1; + } else if (arg & EARLY_SYNC) - frm_cfg.tr_delay = 0; + frm_cfg.rec_delay = frm_cfg.tr_delay = 0; + else if (arg & SECOND_WORD_SYNC) { + frm_cfg.rec_delay = 7; + frm_cfg.tr_delay = 1; + } tr_cfg.bulk_wspace = frm_cfg.tr_delay; frm_cfg.early_wend = regk_sser_yes; @@ -680,9 +832,11 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, else if (arg & SYNC_OFF) frm_cfg.frame_pin_use = regk_sser_gio0; - if (arg & WORD_SIZE_8) + dma_w_size = regk_dma_set_w_size2; + if (arg & WORD_SIZE_8) { rec_cfg.sample_size = tr_cfg.sample_size = 7; - else if (arg & WORD_SIZE_12) + dma_w_size = regk_dma_set_w_size1; + } else if (arg & WORD_SIZE_12) rec_cfg.sample_size = tr_cfg.sample_size = 11; else if (arg & WORD_SIZE_16) rec_cfg.sample_size = tr_cfg.sample_size = 15; @@ -696,10 +850,13 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, else if (arg & BIT_ORDER_LSB) rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_lsbfirst; - if (arg & FLOW_CONTROL_ENABLE) + if (arg & FLOW_CONTROL_ENABLE) { + frm_cfg.status_pin_use = regk_sser_frm; rec_cfg.fifo_thr = regk_sser_thr16; - else if (arg & FLOW_CONTROL_DISABLE) + } else if (arg & FLOW_CONTROL_DISABLE) { + frm_cfg.status_pin_use = regk_sser_gio0; rec_cfg.fifo_thr = regk_sser_inf; + } if (arg & CLOCK_NOT_GATED) gen_cfg.gate_clk = regk_sser_no; @@ -726,9 +883,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, break; case SSP_OPOLARITY: if (arg & CLOCK_NORMAL) - gen_cfg.out_clk_pol = regk_sser_neg; - else if (arg & CLOCK_INVERT) gen_cfg.out_clk_pol = regk_sser_pos; + else if (arg & CLOCK_INVERT) + gen_cfg.out_clk_pol = regk_sser_neg; if (arg & FRAME_NORMAL) frm_cfg.level = regk_sser_pos_hi; @@ -770,10 +927,9 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, } - if (port->started) - { - tr_cfg.tr_en = port->output; + if (port->started) { rec_cfg.rec_en = port->input; + gen_cfg.en = (port->output | port->input); } REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); @@ -782,138 +938,145 @@ static int sync_serial_ioctl(struct inode *inode, struct file *file, REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + + if (cmd == SSP_FRAME_SYNC && (arg & (WORD_SIZE_8 | WORD_SIZE_12 | + WORD_SIZE_16 | WORD_SIZE_24 | WORD_SIZE_32))) { + int en = gen_cfg.en; + gen_cfg.en = 0; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + /* ##### Should DMA be stoped before we change dma size? */ + DMA_WR_CMD(port->regi_dmain, dma_w_size); + DMA_WR_CMD(port->regi_dmaout, dma_w_size); + gen_cfg.en = en; + REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg); + } + spin_unlock_irq(&port->lock); return return_val; } -static ssize_t sync_serial_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) +/* NOTE: sync_serial_write does not support concurrency */ +static ssize_t sync_serial_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) { int dev = iminor(file->f_path.dentry->d_inode); DECLARE_WAITQUEUE(wait, current); - sync_port *port; - unsigned long c, c1; - unsigned long free_outp; - unsigned long outp; - unsigned long out_buffer; + struct sync_port *port; + int trunc_count; unsigned long flags; + int bytes_free; + int out_buf_count; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) - { + unsigned char *rd_ptr; /* First allocated byte in the buffer */ + unsigned char *wr_ptr; /* First free byte in the buffer */ + unsigned char *buf_stop_ptr; /* Last byte + 1 */ + + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; } port = &ports[dev]; - DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE)); - /* Space to end of buffer */ - /* - * out_buffer <c1>012345<- c ->OUT_BUFFER_SIZE - * outp^ +out_count - ^free_outp - * out_buffer 45<- c ->0123OUT_BUFFER_SIZE - * +out_count outp^ - * free_outp - * + /* |<- OUT_BUFFER_SIZE ->| + * |<- out_buf_count ->| + * |<- trunc_count ->| ...->| + * ______________________________________________________ + * | free | data | free | + * |_________|___________________|________________________| + * ^ rd_ptr ^ wr_ptr */ + DEBUGWRITE(printk(KERN_DEBUG "W d%d c %lu a: %p c: %p\n", + port->port_nbr, count, port->active_tr_descr, + port->catch_tr_descr)); /* Read variables that may be updated by interrupts */ spin_lock_irqsave(&port->lock, flags); - count = count > OUT_BUFFER_SIZE - port->out_count ? OUT_BUFFER_SIZE - port->out_count : count; - outp = (unsigned long)port->outp; - free_outp = outp + port->out_count; + rd_ptr = port->out_rd_ptr; + out_buf_count = port->out_buf_count; spin_unlock_irqrestore(&port->lock, flags); - out_buffer = (unsigned long)port->out_buffer; - /* Find out where and how much to write */ - if (free_outp >= out_buffer + OUT_BUFFER_SIZE) - free_outp -= OUT_BUFFER_SIZE; - if (free_outp >= outp) - c = out_buffer + OUT_BUFFER_SIZE - free_outp; - else - c = outp - free_outp; - if (c > count) - c = count; + /* Check if resources are available */ + if (port->tr_running && + ((port->use_dma && port->active_tr_descr == port->catch_tr_descr) || + out_buf_count >= OUT_BUFFER_SIZE)) { + DEBUGWRITE(printk(KERN_DEBUG "sser%d full\n", dev)); + return -EAGAIN; + } + + buf_stop_ptr = port->out_buffer + OUT_BUFFER_SIZE; + + /* Determine pointer to the first free byte, before copying. */ + wr_ptr = rd_ptr + out_buf_count; + if (wr_ptr >= buf_stop_ptr) + wr_ptr -= OUT_BUFFER_SIZE; -// DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c)); - if (copy_from_user((void*)free_outp, buf, c)) + /* If we wrap the ring buffer, let the user space program handle it by + * truncating the data. This could be more elegant, small buffer + * fragments may occur. + */ + bytes_free = OUT_BUFFER_SIZE - out_buf_count; + if (wr_ptr + bytes_free > buf_stop_ptr) + bytes_free = buf_stop_ptr - wr_ptr; + trunc_count = (count < bytes_free) ? count : bytes_free; + + if (copy_from_user(wr_ptr, buf, trunc_count)) return -EFAULT; - if (c != count) { - buf += c; - c1 = count - c; - DEBUGWRITE(printk("w2 fi %lu c %lu c1 %lu\n", free_outp-out_buffer, c, c1)); - if (copy_from_user((void*)out_buffer, buf, c1)) - return -EFAULT; - } - spin_lock_irqsave(&port->lock, flags); - port->out_count += count; - spin_unlock_irqrestore(&port->lock, flags); + DEBUGOUTBUF(printk(KERN_DEBUG "%-4d + %-4d = %-4d %p %p %p\n", + out_buf_count, trunc_count, + port->out_buf_count, port->out_buffer, + wr_ptr, buf_stop_ptr)); /* Make sure transmitter/receiver is running */ - if (!port->started) - { + if (!port->started) { reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg); - reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg); reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg); cfg.en = regk_sser_yes; - tr_cfg.tr_en = port->output; rec_cfg.rec_en = port->input; REG_WR(sser, port->regi_sser, rw_cfg, cfg); - REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg); port->started = 1; } - if (file->f_flags & O_NONBLOCK) { - spin_lock_irqsave(&port->lock, flags); - if (!port->tr_running) { - if (!port->use_dma) { - reg_sser_rw_intr_mask intr_mask; - intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); - /* Start sender by writing data */ - send_word(port); - /* and enable transmitter ready IRQ */ - intr_mask.trdy = 1; - REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); - } else { - start_dma(port, (unsigned char* volatile )port->outp, c); - } - } - spin_unlock_irqrestore(&port->lock, flags); - DEBUGWRITE(printk("w d%d c %lu NB\n", - port->port_nbr, count)); - return count; + /* Setup wait if blocking */ + if (!(file->f_flags & O_NONBLOCK)) { + add_wait_queue(&port->out_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); } - /* Sleep until all sent */ - - add_wait_queue(&port->out_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); spin_lock_irqsave(&port->lock, flags); - if (!port->tr_running) { - if (!port->use_dma) { - reg_sser_rw_intr_mask intr_mask; - intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); - /* Start sender by writing data */ - send_word(port); - /* and enable transmitter ready IRQ */ - intr_mask.trdy = 1; - REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); - } else { - start_dma(port, port->outp, c); - } + port->out_buf_count += trunc_count; + if (port->use_dma) { + start_dma_out(port, wr_ptr, trunc_count); + } else if (!port->tr_running) { + reg_sser_rw_intr_mask intr_mask; + intr_mask = REG_RD(sser, port->regi_sser, rw_intr_mask); + /* Start sender by writing data */ + send_word(port); + /* and enable transmitter ready IRQ */ + intr_mask.trdy = 1; + REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask); } spin_unlock_irqrestore(&port->lock, flags); + + /* Exit if non blocking */ + if (file->f_flags & O_NONBLOCK) { + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu %08x\n", + port->port_nbr, trunc_count, + REG_RD_INT(dma, port->regi_dmaout, r_intr))); + return trunc_count; + } + schedule(); set_current_state(TASK_RUNNING); remove_wait_queue(&port->out_wait_q, &wait); + if (signal_pending(current)) - { return -EINTR; - } - DEBUGWRITE(printk("w d%d c %lu\n", port->port_nbr, count)); - return count; + + DEBUGWRITE(printk(KERN_DEBUG "w d%d c %lu\n", + port->port_nbr, trunc_count)); + return trunc_count; } static ssize_t sync_serial_read(struct file * file, char * buf, @@ -926,7 +1089,7 @@ static ssize_t sync_serial_read(struct file * file, char * buf, unsigned char* end; unsigned long flags; - if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled) + if (dev < 0 || dev >= NBR_PORTS || !ports[dev].enabled) { DEBUG(printk("Invalid minor %d\n", dev)); return -ENODEV; @@ -949,7 +1112,6 @@ static ssize_t sync_serial_read(struct file * file, char * buf, port->started = 1; } - /* Calculate number of available bytes */ /* Save pointers to avoid that they are modified by interrupt */ spin_lock_irqsave(&port->lock, flags); @@ -958,16 +1120,14 @@ static ssize_t sync_serial_read(struct file * file, char * buf, spin_unlock_irqrestore(&port->lock, flags); while ((start == end) && !port->full) /* No data */ { + DEBUGREAD(printk(KERN_DEBUG "&")); if (file->f_flags & O_NONBLOCK) - { return -EAGAIN; - } interruptible_sleep_on(&port->in_wait_q); if (signal_pending(current)) - { return -EINTR; - } + spin_lock_irqsave(&port->lock, flags); start = (unsigned char*)port->readp; /* cast away volatile */ end = (unsigned char*)port->writep; /* cast away volatile */ @@ -1004,83 +1164,105 @@ static void send_word(sync_port* port) switch(tr_cfg.sample_size) { case 8: - port->out_count--; - tr_data.data = *port->outp++; + port->out_buf_count--; + tr_data.data = *port->out_rd_ptr++; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 12: { - int data = (*port->outp++) << 8; - data |= *port->outp++; - port->out_count-=2; + int data = (*port->out_rd_ptr++) << 8; + data |= *port->out_rd_ptr++; + port->out_buf_count -= 2; tr_data.data = data; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; } break; case 16: - port->out_count-=2; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 2; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + port->out_rd_ptr += 2; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 24: - port->out_count-=3; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 3; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - tr_data.data = *port->outp++; + port->out_rd_ptr += 2; + tr_data.data = *port->out_rd_ptr++; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; case 32: - port->out_count-=4; - tr_data.data = *(unsigned short *)port->outp; + port->out_buf_count -= 4; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - tr_data.data = *(unsigned short *)port->outp; + port->out_rd_ptr += 2; + tr_data.data = *(unsigned short *)port->out_rd_ptr; REG_WR(sser, port->regi_sser, rw_tr_data, tr_data); - port->outp+=2; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; + port->out_rd_ptr += 2; + if (port->out_rd_ptr >= port->out_buffer + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; break; } } - -static void start_dma(struct sync_port* port, const char* data, int count) +static void start_dma_out(struct sync_port *port, + const char *data, int count) { - port->tr_running = 1; - port->out_descr.buf = (char*)virt_to_phys((char*)data); - port->out_descr.after = port->out_descr.buf + count; - port->out_descr.eol = port->out_descr.intr = 1; + port->active_tr_descr->buf = (char *) virt_to_phys((char *) data); + port->active_tr_descr->after = port->active_tr_descr->buf + count; + port->active_tr_descr->intr = 1; + + port->active_tr_descr->eol = 1; + port->prev_tr_descr->eol = 0; + + DEBUGTRDMA(printk(KERN_DEBUG "Inserting eolr:%p eol@:%p\n", + port->prev_tr_descr, port->active_tr_descr)); + port->prev_tr_descr = port->active_tr_descr; + port->active_tr_descr = phys_to_virt((int) port->active_tr_descr->next); + + if (!port->tr_running) { + reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, + rw_tr_cfg); - port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr); - port->out_context.saved_data_buf = port->out_descr.buf; + port->out_context.next = 0; + port->out_context.saved_data = + (dma_descr_data *)virt_to_phys(port->prev_tr_descr); + port->out_context.saved_data_buf = port->prev_tr_descr->buf; + + DMA_START_CONTEXT(port->regi_dmaout, + virt_to_phys((char *)&port->out_context)); + + tr_cfg.tr_en = regk_sser_yes; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); + DEBUGTRDMA(printk(KERN_DEBUG "dma s\n");); + } else { + DMA_CONTINUE_DATA(port->regi_dmaout); + DEBUGTRDMA(printk(KERN_DEBUG "dma c\n");); + } - DMA_START_CONTEXT(port->regi_dmaout, virt_to_phys((char*)&port->out_context)); - DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count)); + port->tr_running = 1; } -static void start_dma_in(sync_port* port) +static void start_dma_in(sync_port *port) { int i; - char* buf; + char *buf; port->writep = port->flip; - if (port->writep > port->flip + port->in_buffer_size) - { + if (port->writep > port->flip + port->in_buffer_size) { panic("Offset too large in sync serial driver\n"); return; } buf = (char*)virt_to_phys(port->in_buffer); - for (i = 0; i < NUM_IN_DESCR; i++) { + for (i = 0; i < NBR_IN_DESCR; i++) { port->in_descr[i].buf = buf; port->in_descr[i].after = buf + port->inbufchunk; port->in_descr[i].intr = 1; @@ -1092,59 +1274,126 @@ static void start_dma_in(sync_port* port) port->in_descr[i-1].next = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); port->in_descr[i-1].eol = regk_sser_yes; port->next_rx_desc = &port->in_descr[0]; - port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1]; + port->prev_rx_desc = &port->in_descr[NBR_IN_DESCR - 1]; port->in_context.saved_data = (dma_descr_data*)virt_to_phys(&port->in_descr[0]); port->in_context.saved_data_buf = port->in_descr[0].buf; DMA_START_CONTEXT(port->regi_dmain, virt_to_phys(&port->in_context)); } #ifdef SYNC_SER_DMA -static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t tr_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; + reg_dma_rw_stat stat; int i; - struct dma_descr_data *descr; - unsigned int sentl; int found = 0; + int stop_sser = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) - { + for (i = 0; i < NBR_PORTS; i++) { sync_port *port = &ports[i]; - if (!port->enabled || !port->use_dma ) + if (!port->enabled || !port->use_dma) continue; + /* IRQ active for the port? */ masked = REG_RD(dma, port->regi_dmaout, r_masked_intr); + if (!masked.data) + continue; - if (masked.data) /* IRQ active for the port? */ - { - found = 1; - /* Clear IRQ */ - REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr); - descr = &port->out_descr; - sentl = descr->after - descr->buf; - port->out_count -= sentl; - port->outp += sentl; - if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE) - port->outp = port->out_buffer; - if (port->out_count) { - int c; - c = port->out_buffer + OUT_BUFFER_SIZE - port->outp; - if (c > port->out_count) - c = port->out_count; - DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c)); - start_dma(port, port->outp, c); - } else { - DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl)); - port->tr_running = 0; + found = 1; + + /* Check if we should stop the DMA transfer */ + stat = REG_RD(dma, port->regi_dmaout, rw_stat); + if (stat.list_state == regk_dma_data_at_eol) + stop_sser = 1; + + /* Clear IRQ */ + REG_WR(dma, port->regi_dmaout, rw_ack_intr, ack_intr); + + if (!stop_sser) { + /* The DMA has completed a descriptor, EOL was not + * encountered, so step relevant descriptor and + * datapointers forward. */ + int sent; + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGTXINT(printk(KERN_DEBUG "%-4d - %-4d = %-4d\t" + "in descr %p (ac: %p)\n", + port->out_buf_count, sent, + port->out_buf_count - sent, + port->catch_tr_descr, + port->active_tr_descr);); + port->out_buf_count -= sent; + port->catch_tr_descr = + phys_to_virt((int) port->catch_tr_descr->next); + port->out_rd_ptr = + phys_to_virt((int) port->catch_tr_descr->buf); + } else { + int i, sent; + /* EOL handler. + * Note that if an EOL was encountered during the irq + * locked section of sync_ser_write the DMA will be + * restarted and the eol flag will be cleared. + * The remaining descriptors will be traversed by + * the descriptor interrupts as usual. + */ + i = 0; + while (!port->catch_tr_descr->eol) { + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGOUTBUF(printk(KERN_DEBUG + "traversing descr %p -%d (%d)\n", + port->catch_tr_descr, + sent, + port->out_buf_count)); + port->out_buf_count -= sent; + port->catch_tr_descr = phys_to_virt( + (int)port->catch_tr_descr->next); + i++; + if (i >= NBR_OUT_DESCR) { + /* TODO: Reset and recover */ + panic("sync_serial: missing eol"); + } } - wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */ + sent = port->catch_tr_descr->after - + port->catch_tr_descr->buf; + DEBUGOUTBUF(printk(KERN_DEBUG + "eol at descr %p -%d (%d)\n", + port->catch_tr_descr, + sent, + port->out_buf_count)); + + port->out_buf_count -= sent; + + /* Update read pointer to first free byte, we + * may already be writing data there. */ + port->out_rd_ptr = + phys_to_virt((int) port->catch_tr_descr->after); + if (port->out_rd_ptr > port->out_buffer + + OUT_BUFFER_SIZE) + port->out_rd_ptr = port->out_buffer; + + reg_sser_rw_tr_cfg tr_cfg = + REG_RD(sser, port->regi_sser, rw_tr_cfg); + DEBUGTXINT(printk(KERN_DEBUG + "tr_int DMA stop %d, set catch @ %p\n", + port->out_buf_count, + port->active_tr_descr)); + if (port->out_buf_count != 0) + printk(KERN_CRIT "sync_ser: buffer not " + "empty after eol.\n"); + port->catch_tr_descr = port->active_tr_descr; + port->tr_running = 0; + tr_cfg.tr_en = regk_sser_no; + REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg); } + /* wake up the waiting process */ + wake_up_interruptible(&port->out_wait_q); } return IRQ_RETVAL(found); } /* tr_interrupt */ -static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t rx_interrupt(int irq, void *dev_id) { reg_dma_r_masked_intr masked; reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes}; @@ -1152,7 +1401,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) int i; int found = 0; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NBR_PORTS; i++) { sync_port *port = &ports[i]; @@ -1166,7 +1415,7 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) found = 1; while (REG_RD(dma, port->regi_dmain, rw_data) != virt_to_phys(port->next_rx_desc)) { - + DEBUGRXINT(printk(KERN_DEBUG "!")); if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) { int first_size = port->flip + port->in_buffer_size - port->writep; memcpy((char*)port->writep, phys_to_virt((unsigned)port->next_rx_desc->buf), first_size); @@ -1185,11 +1434,16 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) port->full = 1; } - port->next_rx_desc->eol = 0; - port->prev_rx_desc->eol = 1; - port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc); + port->next_rx_desc->eol = 1; + port->prev_rx_desc->eol = 0; + /* Cache bug workaround */ + flush_dma_descr(port->prev_rx_desc, 0); + port->prev_rx_desc = port->next_rx_desc; port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next); - wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */ + /* Cache bug workaround */ + flush_dma_descr(port->prev_rx_desc, 1); + /* wake up the waiting process */ + wake_up_interruptible(&port->in_wait_q); DMA_CONTINUE(port->regi_dmain); REG_WR(dma, port->regi_dmain, rw_ack_intr, ack_intr); @@ -1201,15 +1455,15 @@ static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) #endif /* SYNC_SER_DMA */ #ifdef SYNC_SER_MANUAL -static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs) +static irqreturn_t manual_interrupt(int irq, void *dev_id) { int i; int found = 0; reg_sser_r_masked_intr masked; - for (i = 0; i < NUMBER_OF_PORTS; i++) + for (i = 0; i < NBR_PORTS; i++) { - sync_port* port = &ports[i]; + sync_port *port = &ports[i]; if (!port->enabled || port->use_dma) { @@ -1263,7 +1517,7 @@ static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs if (masked.trdy) /* Transmitter ready? */ { found = 1; - if (port->out_count > 0) /* More data to send */ + if (port->out_buf_count > 0) /* More data to send */ send_word(port); else /* transmission finished */ { diff --git a/arch/cris/arch-v32/kernel/Makefile b/arch/cris/arch-v32/kernel/Makefile index 5d5b613cde8..993d987b007 100644 --- a/arch/cris/arch-v32/kernel/Makefile +++ b/arch/cris/arch-v32/kernel/Makefile @@ -1,4 +1,3 @@ -# $Id: Makefile,v 1.11 2004/12/17 10:16:13 starvik Exp $ # # Makefile for the linux kernel. # @@ -6,9 +5,9 @@ extra-y := head.o -obj-y := entry.o traps.o irq.o debugport.o dma.o pinmux.o \ +obj-y := entry.o traps.o irq.o debugport.o \ process.o ptrace.o setup.o signal.o traps.o time.o \ - arbiter.o io.o + cache.o cacheflush.o obj-$(CONFIG_ETRAXFS_SIM) += vcs_hook.o diff --git a/arch/cris/arch-v32/kernel/arbiter.c b/arch/cris/arch-v32/kernel/arbiter.c deleted file mode 100644 index 420a5312ed0..00000000000 --- a/arch/cris/arch-v32/kernel/arbiter.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Memory arbiter functions. Allocates bandwidth through the - * arbiter and sets up arbiter breakpoints. - * - * The algorithm first assigns slots to the clients that has specified - * bandwidth (e.g. ethernet) and then the remaining slots are divided - * on all the active clients. - * - * Copyright (c) 2004, 2005 Axis Communications AB. - */ - -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/marb_defs.h> -#include <asm/arch/arbiter.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <linux/interrupt.h> -#include <linux/signal.h> -#include <linux/errno.h> -#include <linux/spinlock.h> -#include <asm/io.h> - -struct crisv32_watch_entry -{ - unsigned long instance; - watch_callback* cb; - unsigned long start; - unsigned long end; - int used; -}; - -#define NUMBER_OF_BP 4 -#define NBR_OF_CLIENTS 14 -#define NBR_OF_SLOTS 64 -#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ -#define INTMEM_BANDWIDTH 400000000 -#define NBR_OF_REGIONS 2 - -static struct crisv32_watch_entry watches[NUMBER_OF_BP] = -{ - {regi_marb_bp0}, - {regi_marb_bp1}, - {regi_marb_bp2}, - {regi_marb_bp3} -}; - -static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; -static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; -static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; - -DEFINE_SPINLOCK(arbiter_lock); - -static irqreturn_t -crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs); - -static void crisv32_arbiter_config(int region) -{ - int slot; - int client; - int interval = 0; - int val[NBR_OF_SLOTS]; - - for (slot = 0; slot < NBR_OF_SLOTS; slot++) - val[slot] = NBR_OF_CLIENTS + 1; - - for (client = 0; client < NBR_OF_CLIENTS; client++) - { - int pos; - if (!requested_slots[region][client]) - continue; - interval = NBR_OF_SLOTS / requested_slots[region][client]; - pos = 0; - while (pos < NBR_OF_SLOTS) - { - if (val[pos] != NBR_OF_CLIENTS + 1) - pos++; - else - { - val[pos] = client; - pos += interval; - } - } - } - - client = 0; - for (slot = 0; slot < NBR_OF_SLOTS; slot++) - { - if (val[slot] == NBR_OF_CLIENTS + 1) - { - int first = client; - while(!active_clients[region][client]) { - client = (client + 1) % NBR_OF_CLIENTS; - if (client == first) - break; - } - val[slot] = client; - client = (client + 1) % NBR_OF_CLIENTS; - } - if (region == EXT_REGION) - REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]); - else if (region == INT_REGION) - REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]); - } -} - -extern char _stext, _etext; - -static void crisv32_arbiter_init(void) -{ - static int initialized = 0; - - if (initialized) - return; - - initialized = 1; - - /* CPU caches are active. */ - active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; - crisv32_arbiter_config(EXT_REGION); - crisv32_arbiter_config(INT_REGION); - - if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED, - "arbiter", NULL)) - printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); - -#ifndef CONFIG_ETRAX_KGDB - /* Global watch for writes to kernel text segment. */ - crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, - arbiter_all_clients, arbiter_all_write, NULL); -#endif -} - - - -int crisv32_arbiter_allocate_bandwidth(int client, int region, - unsigned long bandwidth) -{ - int i; - int total_assigned = 0; - int total_clients = 0; - int req; - - crisv32_arbiter_init(); - - for (i = 0; i < NBR_OF_CLIENTS; i++) - { - total_assigned += requested_slots[region][i]; - total_clients += active_clients[region][i]; - } - req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); - - if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS) - return -ENOMEM; - - active_clients[region][client] = 1; - requested_slots[region][client] = req; - crisv32_arbiter_config(region); - - return 0; -} - -int crisv32_arbiter_watch(unsigned long start, unsigned long size, - unsigned long clients, unsigned long accesses, - watch_callback* cb) -{ - int i; - - crisv32_arbiter_init(); - - if (start > 0x80000000) { - printk("Arbiter: %lX doesn't look like a physical address", start); - return -EFAULT; - } - - spin_lock(&arbiter_lock); - - for (i = 0; i < NUMBER_OF_BP; i++) { - if (!watches[i].used) { - reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); - - watches[i].used = 1; - watches[i].start = start; - watches[i].end = start + size; - watches[i].cb = cb; - - REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, watches[i].start); - REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, watches[i].end); - REG_WR_INT(marb_bp, watches[i].instance, rw_op, accesses); - REG_WR_INT(marb_bp, watches[i].instance, rw_clients, clients); - - if (i == 0) - intr_mask.bp0 = regk_marb_yes; - else if (i == 1) - intr_mask.bp1 = regk_marb_yes; - else if (i == 2) - intr_mask.bp2 = regk_marb_yes; - else if (i == 3) - intr_mask.bp3 = regk_marb_yes; - - REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); - spin_unlock(&arbiter_lock); - - return i; - } - } - spin_unlock(&arbiter_lock); - return -ENOMEM; -} - -int crisv32_arbiter_unwatch(int id) -{ - reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); - - crisv32_arbiter_init(); - - spin_lock(&arbiter_lock); - - if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { - spin_unlock(&arbiter_lock); - return -EINVAL; - } - - memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); - - if (id == 0) - intr_mask.bp0 = regk_marb_no; - else if (id == 1) - intr_mask.bp2 = regk_marb_no; - else if (id == 2) - intr_mask.bp2 = regk_marb_no; - else if (id == 3) - intr_mask.bp3 = regk_marb_no; - - REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); - - spin_unlock(&arbiter_lock); - return 0; -} - -extern void show_registers(struct pt_regs *regs); - -static irqreturn_t -crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs) -{ - reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr); - reg_marb_bp_r_brk_clients r_clients; - reg_marb_bp_r_brk_addr r_addr; - reg_marb_bp_r_brk_op r_op; - reg_marb_bp_r_brk_first_client r_first; - reg_marb_bp_r_brk_size r_size; - reg_marb_bp_rw_ack ack = {0}; - reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1}; - struct crisv32_watch_entry* watch; - - if (masked_intr.bp0) { - watch = &watches[0]; - ack_intr.bp0 = regk_marb_yes; - } else if (masked_intr.bp1) { - watch = &watches[1]; - ack_intr.bp1 = regk_marb_yes; - } else if (masked_intr.bp2) { - watch = &watches[2]; - ack_intr.bp2 = regk_marb_yes; - } else if (masked_intr.bp3) { - watch = &watches[3]; - ack_intr.bp3 = regk_marb_yes; - } else { - return IRQ_NONE; - } - - /* Retrieve all useful information and print it. */ - r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); - r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); - r_op = REG_RD(marb_bp, watch->instance, r_brk_op); - r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); - r_size = REG_RD(marb_bp, watch->instance, r_brk_size); - - printk("Arbiter IRQ\n"); - printk("Clients %X addr %X op %X first %X size %X\n", - REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), - REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); - - REG_WR(marb_bp, watch->instance, rw_ack, ack); - REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); - - printk("IRQ occured at %lX\n", regs->erp); - - if (watch->cb) - watch->cb(); - - - return IRQ_HANDLED; -} diff --git a/arch/cris/arch-v32/kernel/crisksyms.c b/arch/cris/arch-v32/kernel/crisksyms.c index e513da71124..77d02c15a7f 100644 --- a/arch/cris/arch-v32/kernel/crisksyms.c +++ b/arch/cris/arch-v32/kernel/crisksyms.c @@ -2,7 +2,8 @@ #include <linux/irq.h> #include <asm/arch/dma.h> #include <asm/arch/intmem.h> -#include <asm/arch/pinmux.h> +#include <asm/arch/mach/pinmux.h> +#include <asm/arch/io.h> /* Functions for allocating DMA channels */ EXPORT_SYMBOL(crisv32_request_dma); @@ -16,7 +17,11 @@ EXPORT_SYMBOL(crisv32_intmem_virt_to_phys); /* Functions for handling pinmux */ EXPORT_SYMBOL(crisv32_pinmux_alloc); +EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed); EXPORT_SYMBOL(crisv32_pinmux_dealloc); +EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed); +EXPORT_SYMBOL(crisv32_io_get_name); +EXPORT_SYMBOL(crisv32_io_get); /* Functions masking/unmasking interrupts */ EXPORT_SYMBOL(mask_irq); diff --git a/arch/cris/arch-v32/kernel/debugport.c b/arch/cris/arch-v32/kernel/debugport.c index d1272ad9215..15af4c29315 100644 --- a/arch/cris/arch-v32/kernel/debugport.c +++ b/arch/cris/arch-v32/kernel/debugport.c @@ -4,17 +4,12 @@ #include <linux/console.h> #include <linux/init.h> -#include <linux/major.h> -#include <linux/delay.h> -#include <linux/tty.h> #include <asm/system.h> -#include <asm/io.h> -#include <asm/arch/hwregs/ser_defs.h> -#include <asm/arch/hwregs/dma_defs.h> -#include <asm/arch/pinmux.h> - -#include <asm/irq.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/ser_defs.h> +#include <hwregs/dma_defs.h> +#include <asm/arch/mach/pinmux.h> struct dbg_port { @@ -59,45 +54,50 @@ struct dbg_port ports[] = 115200, 'N', 8 - } + }, +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + { + 4, + regi_ser4, + 0, + 115200, + 'N', + 8 + }, +#endif }; static struct dbg_port *port = #if defined(CONFIG_ETRAX_DEBUG_PORT0) -&ports[0]; + &ports[0]; #elif defined(CONFIG_ETRAX_DEBUG_PORT1) -&ports[1]; + &ports[1]; #elif defined(CONFIG_ETRAX_DEBUG_PORT2) -&ports[2]; + &ports[2]; #elif defined(CONFIG_ETRAX_DEBUG_PORT3) -&ports[3]; + &ports[3]; +#elif defined(CONFIG_ETRAX_DEBUG_PORT4) + &ports[4]; #else -NULL; + NULL; #endif #ifdef CONFIG_ETRAX_KGDB static struct dbg_port *kgdb_port = #if defined(CONFIG_ETRAX_KGDB_PORT0) -&ports[0]; + &ports[0]; #elif defined(CONFIG_ETRAX_KGDB_PORT1) -&ports[1]; + &ports[1]; #elif defined(CONFIG_ETRAX_KGDB_PORT2) -&ports[2]; + &ports[2]; #elif defined(CONFIG_ETRAX_KGDB_PORT3) -&ports[3]; + &ports[3]; +#elif defined(CONFIG_ETRAX_KGDB_PORT4) + &ports[4]; #else -NULL; + NULL; #endif #endif -#ifdef CONFIG_ETRAXFS_SIM -extern void print_str( const char *str ); -static char buffer[1024]; -static char msg[] = "Debug: "; -static int buffer_pos = sizeof(msg) - 1; -#endif - -extern struct tty_driver *serial_driver; - static void start_port(struct dbg_port* p) { @@ -114,6 +114,10 @@ start_port(struct dbg_port* p) crisv32_pinmux_alloc_fixed(pinmux_ser2); else if (p->nbr == 3) crisv32_pinmux_alloc_fixed(pinmux_ser3); +#if CONFIG_ETRAX_SERIAL_PORTS == 5 + else if (p->nbr == 4) + crisv32_pinmux_alloc_fixed(pinmux_ser4); +#endif /* Set up serial port registers */ reg_ser_rw_tr_ctrl tr_ctrl = {0}; @@ -156,124 +160,21 @@ start_port(struct dbg_port* p) REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl); } -/* No debug */ -#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - return; -} - -/* Target debug */ -#elif !defined(CONFIG_ETRAXFS_SIM) - -static void -console_write_direct(struct console *co, const char *buf, unsigned int len) -{ - int i; - reg_ser_r_stat_din stat; - reg_ser_rw_tr_dma_en tr_dma_en, old; - - /* Switch to manual mode */ - tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en); - if (tr_dma_en.en == regk_ser_yes) { - tr_dma_en.en = regk_ser_no; - REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en); - } - - /* Send data */ - for (i = 0; i < len; i++) { - /* LF -> CRLF */ - if (buf[i] == '\n') { - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, '\r'); - } - /* Wait until transmitter is ready and send.*/ - do { - stat = REG_RD (ser, port->instance, r_stat_din); - } while (!stat.tr_rdy); - REG_WR_INT (ser, port->instance, rw_dout, buf[i]); - } - - /* Restore mode */ - if (tr_dma_en.en != old.en) - REG_WR(ser, port->instance, rw_tr_dma_en, old); -} - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - if (!port) - return; - console_write_direct(co, buf, len); -} - - - -#else - -/* VCS debug */ - -static void -console_write(struct console *co, const char *buf, unsigned int len) -{ - char* pos; - pos = memchr(buf, '\n', len); - if (pos) { - int l = ++pos - buf; - memcpy(buffer + buffer_pos, buf, l); - memcpy(buffer, msg, sizeof(msg) - 1); - buffer[buffer_pos + l] = '\0'; - print_str(buffer); - buffer_pos = sizeof(msg) - 1; - if (pos - buf != len) { - memcpy(buffer + buffer_pos, pos, len - l); - buffer_pos += len - l; - } - } else { - memcpy(buffer + buffer_pos, buf, len); - buffer_pos += len; - } -} - -#endif - -int raw_printk(const char *fmt, ...) -{ - static char buf[1024]; - int printed_len; - va_list args; - va_start(args, fmt); - printed_len = vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - console_write(NULL, buf, strlen(buf)); - return printed_len; -} - -void -stupid_debug(char* buf) -{ - console_write(NULL, buf, strlen(buf)); -} - #ifdef CONFIG_ETRAX_KGDB /* Use polling to get a single character from the kernel debug port */ int getDebugChar(void) { - reg_ser_rs_status_data stat; + reg_ser_rs_stat_din stat; reg_ser_rw_ack_intr ack_intr = { 0 }; do { - stat = REG_RD(ser, kgdb_instance, rs_status_data); - } while (!stat.data_avail); + stat = REG_RD(ser, kgdb_port->instance, rs_stat_din); + } while (!stat.dav); /* Ack the data_avail interrupt. */ - ack_intr.data_avail = 1; - REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr); + ack_intr.dav = 1; + REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr); return stat.data; } @@ -282,173 +183,18 @@ getDebugChar(void) void putDebugChar(int val) { - reg_ser_r_status_data stat; + reg_ser_r_stat_din stat; do { - stat = REG_RD (ser, kgdb_instance, r_status_data); - } while (!stat.tr_ready); - REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val)); + stat = REG_RD(ser, kgdb_port->instance, r_stat_din); + } while (!stat.tr_rdy); + REG_WR_INT(ser, kgdb_port->instance, rw_dout, val); } #endif /* CONFIG_ETRAX_KGDB */ -static int __init -console_setup(struct console *co, char *options) -{ - char* s; - - if (options) { - port = &ports[co->index]; - port->baudrate = 115200; - port->parity = 'N'; - port->bits = 8; - port->baudrate = simple_strtoul(options, NULL, 10); - s = options; - while(*s >= '0' && *s <= '9') - s++; - if (*s) port->parity = *s++; - if (*s) port->bits = *s++ - '0'; - port->started = 0; - start_port(port); - } - return 0; -} - -/* This is a dummy serial device that throws away anything written to it. - * This is used when no debug output is wanted. - */ -static struct tty_driver dummy_driver; - -static int dummy_open(struct tty_struct *tty, struct file * filp) -{ - return 0; -} - -static void dummy_close(struct tty_struct *tty, struct file * filp) -{ -} - -static int dummy_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - return count; -} - -static int -dummy_write_room(struct tty_struct *tty) -{ - return 8192; -} - -void __init -init_dummy_console(void) -{ - memset(&dummy_driver, 0, sizeof(struct tty_driver)); - dummy_driver.driver_name = "serial"; - dummy_driver.name = "ttyS"; - dummy_driver.major = TTY_MAJOR; - dummy_driver.minor_start = 68; - dummy_driver.num = 1; /* etrax100 has 4 serial ports */ - dummy_driver.type = TTY_DRIVER_TYPE_SERIAL; - dummy_driver.subtype = SERIAL_TYPE_NORMAL; - dummy_driver.init_termios = tty_std_termios; - dummy_driver.init_termios.c_cflag = - B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ - dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - - dummy_driver.open = dummy_open; - dummy_driver.close = dummy_close; - dummy_driver.write = dummy_write; - dummy_driver.write_room = dummy_write_room; - if (tty_register_driver(&dummy_driver)) - panic("Couldn't register dummy serial driver\n"); -} - -static struct tty_driver* -crisv32_console_device(struct console* co, int *index) -{ - if (port) - *index = port->nbr; - return port ? serial_driver : &dummy_driver; -} - -static struct console sercons = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : -1, - cflag : 0, - next : NULL -}; -static struct console sercons0 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 0, - cflag : 0, - next : NULL -}; - -static struct console sercons1 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 1, - cflag : 0, - next : NULL -}; -static struct console sercons2 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 2, - cflag : 0, - next : NULL -}; -static struct console sercons3 = { - name : "ttyS", - write: console_write, - read : NULL, - device : crisv32_console_device, - unblank : NULL, - setup : console_setup, - flags : CON_PRINTBUFFER, - index : 3, - cflag : 0, - next : NULL -}; - /* Register console for printk's, etc. */ int __init init_etrax_debug(void) { - static int first = 1; - - if (!first) { - unregister_console(&sercons); - register_console(&sercons0); - register_console(&sercons1); - register_console(&sercons2); - register_console(&sercons3); - init_dummy_console(); - return 0; - } - first = 0; - register_console(&sercons); start_port(port); #ifdef CONFIG_ETRAX_KGDB @@ -456,5 +202,3 @@ init_etrax_debug(void) #endif /* CONFIG_ETRAX_KGDB */ return 0; } - -__initcall(init_etrax_debug); diff --git a/arch/cris/arch-v32/kernel/entry.S b/arch/cris/arch-v32/kernel/entry.S index f9d27807b91..eebbaba4543 100644 --- a/arch/cris/arch-v32/kernel/entry.S +++ b/arch/cris/arch-v32/kernel/entry.S @@ -10,7 +10,7 @@ * after a timer-interrupt and after each system call. * * Stack layout in 'ret_from_system_call': - * ptrace needs to have all regs on the stack. + * ptrace needs to have all regs on the stack. * if the order here is changed, it needs to be * updated in fork.c:copy_process, signal.c:do_signal, * ptrace.c and ptrace.h @@ -281,12 +281,10 @@ _work_notifysig: ;; Deal with pending signals and notify-resume requests. addoq +TI_flags, $r0, $acr - move.d [$acr], $r13 ; The thread_info_flags parameter. - move.d $r9, $r10 ; do_notify_resume syscall/irq param. - moveq 0, $r11 ; oldset param - 0 in this case. - move.d $sp, $r12 ; The regs param. + move.d [$acr], $r12 ; The thread_info_flags parameter. + move.d $sp, $r11 ; The regs param. jsr do_notify_resume - nop + move.d $r9, $r10 ; do_notify_resume syscall/irq param. ba _Rexit nop @@ -396,7 +394,7 @@ nmi_interrupt: btstq REG_BIT(intr_vect, r_nmi, watchdog), $r0 bpl 1f nop - jsr handle_watchdog_bite ; In time.c. + jsr handle_watchdog_bite ; In time.c. move.d $sp, $r10 ; Pointer to registers 1: btstq REG_BIT(intr_vect, r_nmi, ext), $r0 bpl 1f @@ -515,6 +513,13 @@ _ugdb_handle_exception: ba do_sigtrap ; SIGTRAP the offending process. move.d [$sp+], $r0 ; Restore R0 in delay slot. + .global kernel_execve +kernel_execve: + move.d __NR_execve, $r9 + break 13 + ret + nop + .data .section .rodata,"a" @@ -778,21 +783,21 @@ sys_call_table: .long sys_epoll_ctl /* 255 */ .long sys_epoll_wait .long sys_remap_file_pages - .long sys_set_tid_address - .long sys_timer_create - .long sys_timer_settime /* 260 */ - .long sys_timer_gettime - .long sys_timer_getoverrun - .long sys_timer_delete - .long sys_clock_settime - .long sys_clock_gettime /* 265 */ - .long sys_clock_getres - .long sys_clock_nanosleep + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep .long sys_statfs64 .long sys_fstatfs64 .long sys_tgkill /* 270 */ .long sys_utimes - .long sys_fadvise64_64 + .long sys_fadvise64_64 .long sys_ni_syscall /* sys_vserver */ .long sys_ni_syscall /* sys_mbind */ .long sys_ni_syscall /* 275 sys_get_mempolicy */ @@ -805,6 +810,48 @@ sys_call_table: .long sys_mq_getsetattr .long sys_ni_syscall /* reserved for kexec */ .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get /* 290 */ + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat /* 295 */ + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 /* 300 */ + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat /* 305 */ + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare /* 310 */ + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range + .long sys_tee /* 315 */ + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait + .long sys_utimensat /* 320 */ + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_fallocate + .long sys_timerfd_settime /* 325 */ + .long sys_timerfd_gettime /* * NOTE!! This doesn't have to be exact - we just have diff --git a/arch/cris/arch-v32/kernel/fasttimer.c b/arch/cris/arch-v32/kernel/fasttimer.c index b40551f9f40..2de9d5849ef 100644 --- a/arch/cris/arch-v32/kernel/fasttimer.c +++ b/arch/cris/arch-v32/kernel/fasttimer.c @@ -1,110 +1,9 @@ -/* $Id: fasttimer.c,v 1.11 2005/01/04 11:15:46 starvik Exp $ +/* * linux/arch/cris/kernel/fasttimer.c * * Fast timers for ETRAX FS - * This may be useful in other OS than Linux so use 2 space indentation... - * - * $Log: fasttimer.c,v $ - * Revision 1.11 2005/01/04 11:15:46 starvik - * Don't share timer IRQ. - * - * Revision 1.10 2004/12/07 09:19:38 starvik - * Corrected includes. - * Use correct interrupt macros. - * - * Revision 1.9 2004/05/14 10:18:58 starvik - * Export fast_timer_list - * - * Revision 1.8 2004/05/14 07:58:03 starvik - * Merge of changes from 2.4 - * - * Revision 1.7 2003/07/10 12:06:14 starvik - * Return IRQ_NONE if irq wasn't handled - * - * Revision 1.6 2003/07/04 08:27:49 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.5 2003/06/05 10:16:22 johana - * New INTR_VECT macros. - * - * Revision 1.4 2003/06/03 08:49:45 johana - * Fixed typo. - * - * Revision 1.3 2003/06/02 12:51:27 johana - * Now compiles. - * Commented some include files that probably can be removed. - * - * Revision 1.2 2003/06/02 12:09:41 johana - * Ported to ETRAX FS using the trig interrupt instead of timer1. - * - * Revision 1.3 2002/12/12 08:26:32 starvik - * Don't use C-comments inside CVS comments - * - * Revision 1.2 2002/12/11 15:42:02 starvik - * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/ - * - * Revision 1.1 2002/11/18 07:58:06 starvik - * Fast timers (from Linux 2.4) - * - * Revision 1.5 2002/10/15 06:21:39 starvik - * Added call to init_waitqueue_head * - * Revision 1.4 2002/05/28 17:47:59 johana - * Added del_fast_timer() - * - * Revision 1.3 2002/05/28 16:16:07 johana - * Handle empty fast_timer_list - * - * Revision 1.2 2002/05/27 15:38:42 johana - * Made it compile without warnings on Linux 2.4. - * (includes, wait_queue, PROC_FS and snprintf) - * - * Revision 1.1 2002/05/27 15:32:25 johana - * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree. - * - * Revision 1.8 2001/11/27 13:50:40 pkj - * Disable interrupts while stopping the timer and while modifying the - * list of active timers in timer1_handler() as it may be interrupted - * by other interrupts (e.g., the serial interrupt) which may add fast - * timers. - * - * Revision 1.7 2001/11/22 11:50:32 pkj - * * Only store information about the last 16 timers. - * * proc_fasttimer_read() now uses an allocated buffer, since it - * requires more space than just a page even for only writing the - * last 16 timers. The buffer is only allocated on request, so - * unless /proc/fasttimer is read, it is never allocated. - * * Renamed fast_timer_started to fast_timers_started to match - * fast_timers_added and fast_timers_expired. - * * Some clean-up. - * - * Revision 1.6 2000/12/13 14:02:08 johana - * Removed volatile for fast_timer_list - * - * Revision 1.5 2000/12/13 13:55:35 johana - * Added DEBUG_LOG, added som cli() and cleanup - * - * Revision 1.4 2000/12/05 13:48:50 johana - * Added range check when writing proc file, modified timer int handling - * - * Revision 1.3 2000/11/23 10:10:20 johana - * More debug/logging possibilities. - * Moved GET_JIFFIES_USEC() to timex.h and time.c - * - * Revision 1.2 2000/11/01 13:41:04 johana - * Clean up and bugfixes. - * Created new do_gettimeofday_fast() that gets a timeval struct - * with time based on jiffies and *R_TIMER0_DATA, uses a table - * for fast conversion of timer value to microseconds. - * (Much faster the standard do_gettimeofday() and we don't really - * want to use the true time - we want the "uptime" so timers don't screw up - * when we change the time. - * TODO: Add efficient support for continuous timers as well. - * - * Revision 1.1 2000/10/26 15:49:16 johana - * Added fasttimer, highresolution timers. - * - * Copyright (C) 2000,2001 2002, 2003 Axis Communications AB, Lund, Sweden + * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden */ #include <linux/errno.h> @@ -122,9 +21,9 @@ #include <linux/version.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/timer_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> #include <asm/fasttimer.h> #include <linux/proc_fs.h> @@ -140,30 +39,25 @@ #define DEBUG_LOG_INCLUDED #define FAST_TIMER_LOG -//#define FAST_TIMER_TEST +/* #define FAST_TIMER_TEST */ #define FAST_TIMER_SANITY_CHECKS #ifdef FAST_TIMER_SANITY_CHECKS -#define SANITYCHECK(x) x -static int sanity_failed = 0; -#else -#define SANITYCHECK(x) +static int sanity_failed; #endif #define D1(x) #define D2(x) #define DP(x) -#define __INLINE__ inline - -static int fast_timer_running = 0; -static int fast_timers_added = 0; -static int fast_timers_started = 0; -static int fast_timers_expired = 0; -static int fast_timers_deleted = 0; -static int fast_timer_is_init = 0; -static int fast_timer_ints = 0; +static unsigned int fast_timer_running; +static unsigned int fast_timers_added; +static unsigned int fast_timers_started; +static unsigned int fast_timers_expired; +static unsigned int fast_timers_deleted; +static unsigned int fast_timer_is_init; +static unsigned int fast_timer_ints; struct fast_timer *fast_timer_list = NULL; @@ -171,8 +65,8 @@ struct fast_timer *fast_timer_list = NULL; #define DEBUG_LOG_MAX 128 static const char * debug_log_string[DEBUG_LOG_MAX]; static unsigned long debug_log_value[DEBUG_LOG_MAX]; -static int debug_log_cnt = 0; -static int debug_log_cnt_wrapped = 0; +static unsigned int debug_log_cnt; +static unsigned int debug_log_cnt_wrapped; #define DEBUG_LOG(string, value) \ { \ @@ -202,103 +96,92 @@ struct fast_timer timer_expired_log[NUM_TIMER_STATS]; int timer_div_settings[NUM_TIMER_STATS]; int timer_delay_settings[NUM_TIMER_STATS]; +struct work_struct fast_work; static void -timer_trig_handler(void); +timer_trig_handler(struct work_struct *work); /* Not true gettimeofday, only checks the jiffies (uptime) + useconds */ -void __INLINE__ do_gettimeofday_fast(struct timeval *tv) +inline void do_gettimeofday_fast(struct fasttime_t *tv) { - unsigned long sec = jiffies; - unsigned long usec = GET_JIFFIES_USEC(); - - usec += (sec % HZ) * (1000000 / HZ); - sec = sec / HZ; - - if (usec > 1000000) - { - usec -= 1000000; - sec++; - } - tv->tv_sec = sec; - tv->tv_usec = usec; + tv->tv_jiff = jiffies; + tv->tv_usec = GET_JIFFIES_USEC(); } -int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1) +inline int fasttime_cmp(struct fasttime_t *t0, struct fasttime_t *t1) { - if (t0->tv_sec < t1->tv_sec) - { - return -1; - } - else if (t0->tv_sec > t1->tv_sec) - { - return 1; - } - if (t0->tv_usec < t1->tv_usec) - { - return -1; - } - else if (t0->tv_usec > t1->tv_usec) - { - return 1; - } - return 0; + /* Compare jiffies. Takes care of wrapping */ + if (time_before(t0->tv_jiff, t1->tv_jiff)) + return -1; + else if (time_after(t0->tv_jiff, t1->tv_jiff)) + return 1; + + /* Compare us */ + if (t0->tv_usec < t1->tv_usec) + return -1; + else if (t0->tv_usec > t1->tv_usec) + return 1; + return 0; } /* Called with ints off */ -void __INLINE__ start_timer_trig(unsigned long delay_us) +inline void start_timer_trig(unsigned long delay_us) { reg_timer_rw_ack_intr ack_intr = { 0 }; reg_timer_rw_intr_mask intr_mask; reg_timer_rw_trig trig; reg_timer_rw_trig_cfg trig_cfg = { 0 }; - reg_timer_r_time r_time; + reg_timer_r_time r_time0; + reg_timer_r_time r_time1; + unsigned char trig_wrap; + unsigned char time_wrap; - r_time = REG_RD(timer, regi_timer, r_time); + r_time0 = REG_RD(timer, regi_timer0, r_time); D1(printk("start_timer_trig : %d us freq: %i div: %i\n", delay_us, freq_index, div)); /* Clear trig irq */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 0; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); - /* Set timer values */ - /* r_time is 100MHz (10 ns resolution) */ - trig = r_time + delay_us*(1000/10); + /* Set timer values and check if trigger wraps. */ + /* r_time is 100MHz (10 ns resolution) */ + trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0; timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig; timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us; /* Ack interrupt */ ack_intr.trig = 1; - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); /* Start timer */ - REG_WR(timer, regi_timer, rw_trig, trig); + REG_WR(timer, regi_timer0, rw_trig, trig); trig_cfg.tmr = regk_timer_time; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); /* Check if we have already passed the trig time */ - r_time = REG_RD(timer, regi_timer, r_time); - if (r_time < trig) { + r_time1 = REG_RD(timer, regi_timer0, r_time); + time_wrap = r_time1 < r_time0; + + if ((trig_wrap && !time_wrap) || (r_time1 < trig)) { /* No, Enable trig irq */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 1; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); fast_timers_started++; fast_timer_running = 1; - } - else - { + } else { /* We have passed the time, disable trig point, ack intr */ trig_cfg.tmr = regk_timer_off; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); - /* call the int routine directly */ - timer_trig_handler(); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); + /* call the int routine */ + INIT_WORK(&fast_work, timer_trig_handler); + schedule_work(&fast_work); } } @@ -320,22 +203,20 @@ void start_one_shot_timer(struct fast_timer *t, do_gettimeofday_fast(&t->tv_set); tmp = fast_timer_list; - SANITYCHECK({ /* Check so this is not in the list already... */ - while (tmp != NULL) - { - if (tmp == t) - { - printk("timer name: %s data: 0x%08lX already in list!\n", name, data); - sanity_failed++; - return; - } - else - { - tmp = tmp->next; - } - } - tmp = fast_timer_list; - }); +#ifdef FAST_TIMER_SANITY_CHECKS + /* Check so this is not in the list already... */ + while (tmp != NULL) { + if (tmp == t) { + printk(KERN_DEBUG + "timer name: %s data: 0x%08lX already " + "in list!\n", name, data); + sanity_failed++; + goto done; + } else + tmp = tmp->next; + } + tmp = fast_timer_list; +#endif t->delay_us = delay_us; t->function = function; @@ -343,11 +224,10 @@ void start_one_shot_timer(struct fast_timer *t, t->name = name; t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000; - t->tv_expires.tv_sec = t->tv_set.tv_sec + delay_us / 1000000; - if (t->tv_expires.tv_usec > 1000000) - { + t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ; + if (t->tv_expires.tv_usec > 1000000) { t->tv_expires.tv_usec -= 1000000; - t->tv_expires.tv_sec++; + t->tv_expires.tv_jiff += HZ; } #ifdef FAST_TIMER_LOG timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t; @@ -355,15 +235,12 @@ void start_one_shot_timer(struct fast_timer *t, fast_timers_added++; /* Check if this should timeout before anything else */ - if (tmp == NULL || timeval_cmp(&t->tv_expires, &tmp->tv_expires) < 0) - { + if (tmp == NULL || fasttime_cmp(&t->tv_expires, &tmp->tv_expires) < 0) { /* Put first in list and modify the timer value */ t->prev = NULL; t->next = fast_timer_list; if (fast_timer_list) - { fast_timer_list->prev = t; - } fast_timer_list = t; #ifdef FAST_TIMER_LOG timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; @@ -372,10 +249,8 @@ void start_one_shot_timer(struct fast_timer *t, } else { /* Put in correct place in list */ while (tmp->next && - timeval_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) - { + fasttime_cmp(&t->tv_expires, &tmp->next->tv_expires) > 0) tmp = tmp->next; - } /* Insert t after tmp */ t->prev = tmp; t->next = tmp->next; @@ -388,6 +263,7 @@ void start_one_shot_timer(struct fast_timer *t, D2(printk("start_one_shot_timer: %d us done\n", delay_us)); +done: local_irq_restore(flags); } /* start_one_shot_timer */ @@ -431,19 +307,18 @@ int del_fast_timer(struct fast_timer * t) /* Timer interrupt handler for trig interrupts */ static irqreturn_t -timer_trig_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_trig_interrupt(int irq, void *dev_id) { reg_timer_r_masked_intr masked_intr; - /* Check if the timer interrupt is for us (a trig int) */ - masked_intr = REG_RD(timer, regi_timer, r_masked_intr); + masked_intr = REG_RD(timer, regi_timer0, r_masked_intr); if (!masked_intr.trig) return IRQ_NONE; - timer_trig_handler(); + timer_trig_handler(NULL); return IRQ_HANDLED; } -static void timer_trig_handler(void) +static void timer_trig_handler(struct work_struct *work) { reg_timer_rw_ack_intr ack_intr = { 0 }; reg_timer_rw_intr_mask intr_mask; @@ -451,38 +326,45 @@ static void timer_trig_handler(void) struct fast_timer *t; unsigned long flags; + /* We keep interrupts disabled not only when we modify the + * fast timer list, but any time we hold a reference to a + * timer in the list, since del_fast_timer may be called + * from (another) interrupt context. Thus, the only time + * when interrupts are enabled is when calling the timer + * callback function. + */ local_irq_save(flags); /* Clear timer trig interrupt */ - intr_mask = REG_RD(timer, regi_timer, rw_intr_mask); + intr_mask = REG_RD(timer, regi_timer0, rw_intr_mask); intr_mask.trig = 0; - REG_WR(timer, regi_timer, rw_intr_mask, intr_mask); + REG_WR(timer, regi_timer0, rw_intr_mask, intr_mask); /* First stop timer, then ack interrupt */ /* Stop timer */ trig_cfg.tmr = regk_timer_off; - REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg); + REG_WR(timer, regi_timer0, rw_trig_cfg, trig_cfg); /* Ack interrupt */ ack_intr.trig = 1; - REG_WR(timer, regi_timer, rw_ack_intr, ack_intr); + REG_WR(timer, regi_timer0, rw_ack_intr, ack_intr); fast_timer_running = 0; fast_timer_ints++; - local_irq_restore(flags); + fast_timer_function_type *f; + unsigned long d; t = fast_timer_list; - while (t) - { - struct timeval tv; + while (t) { + struct fasttime_t tv; /* Has it really expired? */ do_gettimeofday_fast(&tv); - D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec)); + D1(printk(KERN_DEBUG + "t: %is %06ius\n", tv.tv_jiff, tv.tv_usec)); - if (timeval_cmp(&t->tv_expires, &tv) <= 0) - { + if (fasttime_cmp(&t->tv_expires, &tv) <= 0) { /* Yes it has expired */ #ifdef FAST_TIMER_LOG timer_expired_log[fast_timers_expired % NUM_TIMER_STATS] = *t; @@ -490,84 +372,77 @@ static void timer_trig_handler(void) fast_timers_expired++; /* Remove this timer before call, since it may reuse the timer */ - local_irq_save(flags); if (t->prev) - { t->prev->next = t->next; - } else - { fast_timer_list = t->next; - } if (t->next) - { t->next->prev = t->prev; - } t->prev = NULL; t->next = NULL; - local_irq_restore(flags); - if (t->function != NULL) - { - t->function(t->data); - } - else - { + /* Save function callback data before enabling + * interrupts, since the timer may be removed and we + * don't know how it was allocated (e.g. ->function + * and ->data may become overwritten after deletion + * if the timer was stack-allocated). + */ + f = t->function; + d = t->data; + + if (f != NULL) { + /* Run the callback function with interrupts + * enabled. */ + local_irq_restore(flags); + f(d); + local_irq_save(flags); + } else DEBUG_LOG("!trimertrig %i function==NULL!\n", fast_timer_ints); - } - } - else - { + } else { /* Timer is to early, let's set it again using the normal routines */ D1(printk(".\n")); } - local_irq_save(flags); - if ((t = fast_timer_list) != NULL) - { + t = fast_timer_list; + if (t != NULL) { /* Start next timer.. */ - long us; - struct timeval tv; + long us = 0; + struct fasttime_t tv; do_gettimeofday_fast(&tv); - us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 + - t->tv_expires.tv_usec - tv.tv_usec); - if (us > 0) - { - if (!fast_timer_running) - { + + /* time_after_eq takes care of wrapping */ + if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff)) + us = ((t->tv_expires.tv_jiff - tv.tv_jiff) * + 1000000 / HZ + t->tv_expires.tv_usec - + tv.tv_usec); + + if (us > 0) { + if (!fast_timer_running) { #ifdef FAST_TIMER_LOG timer_started_log[fast_timers_started % NUM_TIMER_STATS] = *t; #endif start_timer_trig(us); } - local_irq_restore(flags); break; - } - else - { + } else { /* Timer already expired, let's handle it better late than never. * The normal loop handles it */ D1(printk("e! %d\n", us)); } } - local_irq_restore(flags); } - if (!t) - { + local_irq_restore(flags); + + if (!t) D1(printk("ttrig stop!\n")); - } } static void wake_up_func(unsigned long data) { -#ifdef DECLARE_WAITQUEUE wait_queue_head_t *sleep_wait_p = (wait_queue_head_t*)data; -#else - struct wait_queue **sleep_wait_p = (struct wait_queue **)data; -#endif wake_up(sleep_wait_p); } @@ -577,28 +452,17 @@ static void wake_up_func(unsigned long data) void schedule_usleep(unsigned long us) { struct fast_timer t; -#ifdef DECLARE_WAITQUEUE wait_queue_head_t sleep_wait; init_waitqueue_head(&sleep_wait); - { - DECLARE_WAITQUEUE(wait, current); -#else - struct wait_queue *sleep_wait = NULL; - struct wait_queue wait = { current, NULL }; -#endif D1(printk("schedule_usleep(%d)\n", us)); - add_wait_queue(&sleep_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us, "usleep"); - schedule(); - set_current_state(TASK_RUNNING); - remove_wait_queue(&sleep_wait, &wait); + /* Uninterruptible sleep on the fast timer. (The condition is + * somewhat redundant since the timer is what wakes us up.) */ + wait_event(sleep_wait, !fast_timer_pending(&t)); + D1(printk("done schedule_usleep(%d)\n", us)); -#ifdef DECLARE_WAITQUEUE - } -#endif } #ifdef CONFIG_PROC_FS @@ -618,20 +482,22 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len unsigned long flags; int i = 0; int num_to_show; - struct timeval tv; + struct fasttime_t tv; struct fast_timer *t, *nextt; static char *bigbuf = NULL; static unsigned long used; - if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE))) - { - used = 0; - bigbuf[0] = '\0'; - return 0; - } - - if (!offset || !used) - { + if (!bigbuf) { + bigbuf = vmalloc(BIG_BUF_SIZE); + if (!bigbuf) { + used = 0; + if (buf) + buf[0] = '\0'; + return 0; + } + } + + if (!offset || !used) { do_gettimeofday_fast(&tv); used = 0; @@ -648,7 +514,7 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len used += sprintf(bigbuf + used, "Fast timer running: %s\n", fast_timer_running ? "yes" : "no"); used += sprintf(bigbuf + used, "Current time: %lu.%06lu\n", - (unsigned long)tv.tv_sec, + (unsigned long)tv.tv_jiff, (unsigned long)tv.tv_usec); #ifdef FAST_TIMER_SANITY_CHECKS used += sprintf(bigbuf + used, "Sanity failed: %i\n", @@ -661,10 +527,8 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len int end_i = debug_log_cnt; i = 0; - if (debug_log_cnt_wrapped) - { + if (debug_log_cnt_wrapped) i = debug_log_cnt; - } while ((i != end_i || (debug_log_cnt_wrapped && !used)) && used+100 < BIG_BUF_SIZE) @@ -697,9 +561,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -719,9 +583,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -739,9 +603,9 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len "d: %6li us data: 0x%08lX" "\n", t->name, - (unsigned long)t->tv_set.tv_sec, + (unsigned long)t->tv_set.tv_jiff, (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, + (unsigned long)t->tv_expires.tv_jiff, (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data @@ -752,26 +616,25 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len used += sprintf(bigbuf + used, "Active timers:\n"); local_irq_save(flags); - local_irq_save(flags); t = fast_timer_list; while (t != NULL && (used+100 < BIG_BUF_SIZE)) { nextt = t->next; local_irq_restore(flags); used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu " - "d: %6li us data: 0x%08lX" -/* " func: 0x%08lX" */ - "\n", - t->name, - (unsigned long)t->tv_set.tv_sec, - (unsigned long)t->tv_set.tv_usec, - (unsigned long)t->tv_expires.tv_sec, - (unsigned long)t->tv_expires.tv_usec, + "d: %6li us data: 0x%08lX" +/* " func: 0x%08lX" */ + "\n", + t->name, + (unsigned long)t->tv_set.tv_jiff, + (unsigned long)t->tv_set.tv_usec, + (unsigned long)t->tv_expires.tv_jiff, + (unsigned long)t->tv_expires.tv_usec, t->delay_us, t->data /* , t->function */ ); - local_irq_disable(); + local_irq_save(flags); if (t->next != nextt) { printk("timer removed!\n"); @@ -800,7 +663,7 @@ static volatile int num_test_timeout = 0; static struct fast_timer tr[10]; static int exp_num[10]; -static struct timeval tv_exp[100]; +static struct fasttime_t tv_exp[100]; static void test_timeout(unsigned long data) { @@ -838,7 +701,7 @@ static void fast_timer_test(void) int prev_num; int j; - struct timeval tv, tv0, tv1, tv2; + struct fasttime_t tv, tv0, tv1, tv2; printk("fast_timer_test() start\n"); do_gettimeofday_fast(&tv); @@ -851,21 +714,22 @@ static void fast_timer_test(void) { do_gettimeofday_fast(&tv_exp[j]); } - printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec); + printk(KERN_DEBUG "fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec); for (j = 0; j < 1000; j++) { - printk("%i %i %i %i %i\n",j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); + printk(KERN_DEBUG "%i %i %i %i %i\n", + j_u[j], j_u[j+1], j_u[j+2], j_u[j+3], j_u[j+4]); j += 4; } for (j = 0; j < 100; j++) { - printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n", - tv_exp[j].tv_sec,tv_exp[j].tv_usec, - tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec, - tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec, - tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec, - tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec); + printk(KERN_DEBUG "%i.%i %i.%i %i.%i %i.%i %i.%i\n", + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, + tv_exp[j+1].tv_jiff, tv_exp[j+1].tv_usec, + tv_exp[j+2].tv_jiff, tv_exp[j+2].tv_usec, + tv_exp[j+3].tv_jiff, tv_exp[j+3].tv_usec, + tv_exp[j+4].tv_jiff, tv_exp[j+4].tv_usec); j += 4; } do_gettimeofday_fast(&tv0); @@ -892,14 +756,15 @@ static void fast_timer_test(void) while (num_test_timeout < i) { if (num_test_timeout != prev_num) - { prev_num = num_test_timeout; - } } do_gettimeofday_fast(&tv2); - printk("Timers started %is %06i\n", tv0.tv_sec, tv0.tv_usec); - printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec); - printk("Timers done %is %06i\n", tv2.tv_sec, tv2.tv_usec); + printk(KERN_INFO "Timers started %is %06i\n", + tv0.tv_jiff, tv0.tv_usec); + printk(KERN_INFO "Timers started at %is %06i\n", + tv1.tv_jiff, tv1.tv_usec); + printk(KERN_INFO "Timers done %is %06i\n", + tv2.tv_jiff, tv2.tv_usec); DP(printk("buf0:\n"); printk(buf0); printk("buf1:\n"); @@ -921,9 +786,9 @@ static void fast_timer_test(void) printk("%-10s set: %6is %06ius exp: %6is %06ius " "data: 0x%08X func: 0x%08X\n", t->name, - t->tv_set.tv_sec, + t->tv_set.tv_jiff, t->tv_set.tv_usec, - t->tv_expires.tv_sec, + t->tv_expires.tv_jiff, t->tv_expires.tv_usec, t->data, t->function @@ -931,10 +796,12 @@ static void fast_timer_test(void) printk(" del: %6ius did exp: %6is %06ius as #%i error: %6li\n", t->delay_us, - tv_exp[j].tv_sec, + tv_exp[j].tv_jiff, tv_exp[j].tv_usec, exp_num[j], - (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec); + (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff) * + 1000000 + tv_exp[j].tv_usec - + t->tv_expires.tv_usec); } proc_fasttimer_read(buf5, NULL, 0, 0, 0); printk("buf5 after all done:\n"); @@ -944,7 +811,7 @@ static void fast_timer_test(void) #endif -void fast_timer_init(void) +int fast_timer_init(void) { /* For some reason, request_irq() hangs when called froom time_init() */ if (!fast_timer_is_init) @@ -952,18 +819,20 @@ void fast_timer_init(void) printk("fast_timer_init()\n"); #ifdef CONFIG_PROC_FS - if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 ))) - fasttimer_proc_entry->read_proc = proc_fasttimer_read; + fasttimer_proc_entry = create_proc_entry("fasttimer", 0, 0); + if (fasttimer_proc_entry) + fasttimer_proc_entry->read_proc = proc_fasttimer_read; #endif /* PROC_FS */ - if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, IRQF_DISABLED, - "fast timer int", NULL)) - { - printk("err: timer1 irq\n"); - } + if (request_irq(TIMER0_INTR_VECT, timer_trig_interrupt, + IRQF_SHARED | IRQF_DISABLED, + "fast timer int", &fast_timer_list)) + printk(KERN_ERR "err: fasttimer irq\n"); fast_timer_is_init = 1; #ifdef FAST_TIMER_TEST printk("do test\n"); fast_timer_test(); #endif } + return 0; } +__initcall(fast_timer_init); diff --git a/arch/cris/arch-v32/kernel/head.S b/arch/cris/arch-v32/kernel/head.S index 20bd80a84e4..2d66a7c320e 100644 --- a/arch/cris/arch-v32/kernel/head.S +++ b/arch/cris/arch-v32/kernel/head.S @@ -4,22 +4,25 @@ * Copyright (C) 2003, Axis Communications AB */ - #define ASSEMBLER_MACROS_ONLY /* * The macros found in mmu_defs_asm.h uses the ## concatenation operator, so * -traditional must not be used when assembling this file. */ -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/asm/mmu_defs_asm.h> -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/config_defs_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/reg_rdwr.h> +#include <asm/arch/memmap.h> +#include <hwregs/intr_vect.h> +#include <hwregs/asm/mmu_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <asm/arch/mach/startup.inc> #define CRAMFS_MAGIC 0x28cd3d45 +#define JHEAD_MAGIC 0x1FF528A6 +#define JHEAD_SIZE 8 #define RAM_INIT_MAGIC 0x56902387 #define COMMAND_LINE_MAGIC 0x87109563 +#define NAND_BOOT_MAGIC 0x9a9db001 ;; NOTE: R8 and R9 carry information from the decompressor (if the ;; kernel was compressed). They must not be used in the code below @@ -30,12 +33,11 @@ .global romfs_start .global romfs_length .global romfs_in_flash + .global nand_boot .global swapper_pg_dir - .global crisv32_nand_boot - .global crisv32_nand_cramfs_offset ;; Dummy section to make it bootable with current VCS simulator -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM .section ".boot", "ax" ba tstart nop @@ -51,33 +53,15 @@ tstart: ;; di - ;; Start clocks for used blocks. - move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1 - move.d [$r1], $r0 - or.d REG_STATE(config, rw_clk_ctrl, cpu, yes) | \ - REG_STATE(config, rw_clk_ctrl, bif, yes) | \ - REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0 - move.d $r0, [$r1] - - ;; Set up waitstates etc - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP1_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP2_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP3_CONFIG, $r1 - move.d $r1, [$r0] - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg), $r0 - move.d CONFIG_ETRAX_MEM_GRP4_CONFIG, $r1 - move.d $r1, [$r0] - -#ifdef CONFIG_ETRAXFS_SIM - ;; Set up minimal flash waitstates - move.d 0, $r10 - move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r11 - move.d $r10, [$r11] + START_CLOCKS + + SETUP_WAIT_STATES + + GIO_INIT + +#ifdef CONFIG_SMP +secondary_cpu_entry: /* Entry point for secondary CPUs */ + di #endif ;; Setup and enable the MMU. Use same configuration for both the data @@ -85,7 +69,7 @@ tstart: ;; ;; Note; 3 cycles is needed for a bank-select to take effect. Further; ;; bank 1 is the instruction MMU, bank 2 is the data MMU. -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0 @@ -93,7 +77,7 @@ tstart: ;; Map the virtual DRAM to the RW eprom area at address 0. ;; Also map 0xa for the hook calls, move.d REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8) \ - | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0) \ + | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) \ | REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa), $r0 #endif @@ -104,7 +88,7 @@ tstart: ;; Enable certain page protections and setup linear mapping ;; for f,e,c,b,4,0. -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM move.d REG_STATE(mmu, rw_mm_cfg, we, on) \ | REG_STATE(mmu, rw_mm_cfg, acc, on) \ | REG_STATE(mmu, rw_mm_cfg, ex, on) \ @@ -183,17 +167,11 @@ tstart: nop nop nop - move $s10, $r0 + move $s12, $r0 cmpq 0, $r0 beq master_cpu nop slave_cpu: - ; A slave waits for cpu_now_booting to be equal to CPU ID. - move.d cpu_now_booting, $r1 -slave_wait: - cmp.d [$r1], $r0 - bne slave_wait - nop ; Time to boot-up. Get stack location provided by master CPU. move.d smp_init_current_idle_thread, $r1 move.d [$r1], $sp @@ -203,9 +181,16 @@ slave_wait: jsr smp_callin nop master_cpu: + /* Set up entry point for secondary CPUs. The boot ROM has set up + * EBP at start of internal memory. The CPU will get there + * later when we issue an IPI to them... */ + move.d MEM_INTMEM_START + IPI_INTR_VECT * 4, $r0 + move.d secondary_cpu_entry, $r1 + move.d $r1, [$r0] #endif -#ifndef CONFIG_ETRAXFS_SIM - ;; Check if starting from DRAM or flash. +#ifndef CONFIG_ETRAX_VCS_SIM + ; Check if starting from DRAM (network->RAM boot or unpacked + ; compressed kernel), or directly from flash. lapcq ., $r0 and.d 0x7fffffff, $r0 ; Mask off the non-cache bit. cmp.d 0x10000, $r0 ; Arbitrary, something above this code. @@ -232,12 +217,13 @@ _inflash: beq _dram_initialized nop -#include "../lib/dram_init.S" +#include "../mach/dram_init.S" _dram_initialized: ;; Copy the text and data section to DRAM. This depends on that the ;; variables used below are correctly set up by the linker script. ;; The calculated value stored in R4 is used below. + ;; Leave the cramfs file system (piggybacked after the kernel) in flash. moveq 0, $r0 ; Source. move.d text_start, $r1 ; Destination. move.d __vmlinux_end, $r2 @@ -249,7 +235,7 @@ _dram_initialized: blo 1b nop - ;; Keep CRAMFS in flash. + ;; Check for cramfs. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] @@ -258,6 +244,7 @@ _dram_initialized: bne 1f nop + ;; Set length and start of cramfs, set romfs_in_flash flag addoq +4, $r4, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -273,35 +260,32 @@ _dram_initialized: nop _inram: - ;; Check if booting from NAND flash (in that case we just remember the offset - ;; into the flash where cramfs should be). - move.d REG_ADDR(config, regi_config, r_bootsel), $r0 - move.d [$r0], $r0 - and.d REG_MASK(config, r_bootsel, boot_mode), $r0 - cmp.d REG_STATE(config, r_bootsel, boot_mode, nand), $r0 - bne move_cramfs - moveq 1,$r0 - move.d crisv32_nand_boot, $r1 - move.d $r0, [$r1] - move.d crisv32_nand_cramfs_offset, $r1 - move.d $r9, [$r1] + ;; Check if booting from NAND flash; if so, set appropriate flags + ;; and move on. + cmp.d NAND_BOOT_MAGIC, $r12 + bne move_cramfs ; not nand, jump moveq 1, $r0 - move.d romfs_in_flash, $r1 + move.d nand_boot, $r1 ; tell axisflashmap we're booting from NAND + move.d $r0, [$r1] + moveq 0, $r0 ; tell axisflashmap romfs is not in + move.d romfs_in_flash, $r1 ; (directly accessed) flash move.d $r0, [$r1] - jump _start_it + jump _start_it ; continue with boot nop move_cramfs: - ;; Move the cramfs after BSS. + ;; kernel is in DRAM. + ;; Must figure out if there is a piggybacked rootfs image or not. + ;; Set romfs_length to 0 => no rootfs image available by default. moveq 0, $r0 move.d romfs_length, $r1 move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM ;; The kernel could have been unpacked to DRAM by the loader, but - ;; the cramfs image could still be inte the flash immediately - ;; following the compressed kernel image. The loaded passes the address - ;; of the bute succeeding the last compressed byte in the flash in + ;; the cramfs image could still be in the flash immediately + ;; following the compressed kernel image. The loader passes the address + ;; of the byte succeeding the last compressed byte in the flash in ;; register R9 when starting the kernel. cmp.d 0x0ffffff8, $r9 bhs _no_romfs_in_flash ; R9 points outside the flash area. @@ -310,11 +294,13 @@ move_cramfs: ba _no_romfs_in_flash nop #endif + ;; cramfs rootfs might to be in flash. Check for it. move.d [$r9], $r0 ; cramfs_super.magic cmp.d CRAMFS_MAGIC, $r0 bne _no_romfs_in_flash nop + ;; found cramfs in flash. set address and size, and romfs_in_flash flag. addoq +4, $r9, $acr move.d [$acr], $r0 move.d romfs_length, $r1 @@ -330,27 +316,43 @@ move_cramfs: nop _no_romfs_in_flash: - ;; Look for cramfs. + ;; No romfs in flash, so look for cramfs, or jffs2 with jhead, + ;; after kernel in RAM, as is the case with network->RAM boot. + ;; For cramfs, partition starts with magic and length. + ;; For jffs2, a jhead is prepended which contains with magic and length. + ;; The jhead is not part of the jffs2 partition however. #ifndef CONFIG_ETRAXFS_SIM move.d __vmlinux_end, $r0 #else move.d __end, $r0 #endif move.d [$r0], $r1 - cmp.d CRAMFS_MAGIC, $r1 - bne 2f + cmp.d CRAMFS_MAGIC, $r1 ; cramfs magic? + beq 2f ; yes, jump + nop + cmp.d JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic? + bne 4f ; no, skip copy nop + addq 4, $r0 ; location of jffs2 size + move.d [$r0+], $r2 ; fetch jffs2 size -> r2 + ; r0 now points to start of jffs2 + ba 3f + nop +2: + addoq +4, $r0, $acr ; location of cramfs size + move.d [$acr], $r2 ; fetch cramfs size -> r2 + ; r0 still points to start of cramfs +3: + ;; Now, move the root fs to after kernel's BSS - addoq +4, $r0, $acr - move.d [$acr], $r2 - move.d _end, $r1 + move.d _end, $r1 ; start of cramfs -> r1 move.d romfs_start, $r3 - move.d $r1, [$r3] + move.d $r1, [$r3] ; store at romfs_start (for axisflashmap) move.d romfs_length, $r3 - move.d $r2, [$r3] + move.d $r2, [$r3] ; store size at romfs_length -#ifndef CONFIG_ETRAXFS_SIM - add.d $r2, $r0 +#ifndef CONFIG_ETRAX_VCS_SIM + add.d $r2, $r0 ; copy from end and downwards add.d $r2, $r1 lsrq 1, $r2 ; Size is in bytes, we copy words. @@ -365,10 +367,17 @@ _no_romfs_in_flash: nop #endif -2: +4: + ;; BSS move done. + ;; Clear romfs_in_flash flag, as we now know romfs is in DRAM + ;; Also clear nand_boot flag; if we got here, we know we've not + ;; booted from NAND flash. moveq 0, $r0 move.d romfs_in_flash, $r1 move.d $r0, [$r1] + moveq 0, $r0 + move.d nand_boot, $r1 + move.d $r0, [$r1] jump _start_it ; Jump to cached code. nop @@ -384,8 +393,8 @@ _start_it: move.d cris_command_line, $r10 or.d 0x80000000, $r11 ; Make it virtual 1: - move.b [$r11+], $r12 - move.b $r12, [$r10+] + move.b [$r11+], $r1 + move.b $r1, [$r10+] subq 1, $r13 bne 1b nop @@ -401,7 +410,7 @@ no_command_line: move.d etrax_irv, $r1 ; Set the exception base register and pointer. move.d $r0, [$r1] -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM ;; Clear the BSS region from _bss_start to _end. move.d __bss_start, $r0 move.d _end, $r1 @@ -411,7 +420,7 @@ no_command_line: nop #endif -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM /* Set the watchdog timeout to something big. Will be removed when */ /* watchdog can be disabled with command line option */ move.d 0x7fffffff, $r10 @@ -423,25 +432,44 @@ no_command_line: move.d __bss_start, $r0 movem [$r0], $r13 +#ifdef CONFIG_ETRAX_L2CACHE + jsr l2cache_init + nop +#endif + jump start_kernel ; Jump to start_kernel() in init/main.c. nop .data etrax_irv: .dword 0 + +; Variables for communication with the Axis flash map driver (axisflashmap), +; and for setting up memory in arch/cris/kernel/setup.c . + +; romfs_start is set to the start of the root file system, if it exists +; in directly accessible memory (i.e. NOR Flash when booting from Flash, +; or RAM when booting directly from a network-downloaded RAM image) romfs_start: .dword 0 + +; romfs_length is set to the size of the root file system image, if it exists +; in directly accessible memory (see romfs_start). Otherwise it is set to 0. romfs_length: .dword 0 + +; romfs_in_flash is set to 1 if the root file system resides in directly +; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot +; or NAND flash boot. romfs_in_flash: .dword 0 -crisv32_nand_boot: - .dword 0 -crisv32_nand_cramfs_offset: + +; nand_boot is set to 1 when the kernel has been booted from NAND flash +nand_boot: .dword 0 swapper_pg_dir = 0xc0002000 .section ".init.data", "aw" -#include "../lib/hw_settings.S" +#include "../mach/hw_settings.S" diff --git a/arch/cris/arch-v32/kernel/io.c b/arch/cris/arch-v32/kernel/io.c deleted file mode 100644 index a22a9e02e09..00000000000 --- a/arch/cris/arch-v32/kernel/io.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Helper functions for I/O pins. - * - * Copyright (c) 2004 Axis Communications AB. - */ - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/string.h> -#include <linux/ctype.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/io.h> -#include <asm/arch/pinmux.h> -#include <asm/arch/hwregs/gio_defs.h> - -struct crisv32_ioport crisv32_ioports[] = -{ - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pa_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pa_din), - 8 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pb_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pb_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pc_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pc_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pd_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pd_din), - 18 - }, - { - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_oe), - (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_dout), - (unsigned long*)REG_ADDR(gio, regi_gio, r_pe_din), - 18 - } -}; - -#define NBR_OF_PORTS ARRAY_SIZE(crisv32_ioports) - -struct crisv32_iopin crisv32_led1_green; -struct crisv32_iopin crisv32_led1_red; -struct crisv32_iopin crisv32_led2_green; -struct crisv32_iopin crisv32_led2_red; -struct crisv32_iopin crisv32_led3_green; -struct crisv32_iopin crisv32_led3_red; - -/* Dummy port used when green LED and red LED is on the same bit */ -static unsigned long io_dummy; -static struct crisv32_ioport dummy_port = -{ - &io_dummy, - &io_dummy, - &io_dummy, - 18 -}; -static struct crisv32_iopin dummy_led = -{ - &dummy_port, - 0 -}; - -static int __init crisv32_io_init(void) -{ - int ret = 0; - /* Initialize LEDs */ - ret += crisv32_io_get_name(&crisv32_led1_green, CONFIG_ETRAX_LED1G); - ret += crisv32_io_get_name(&crisv32_led1_red, CONFIG_ETRAX_LED1R); - ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_LED2G); - ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_LED2R); - ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_LED3G); - ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_LED3R); - crisv32_io_set_dir(&crisv32_led1_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led1_red, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); - crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); - - if (!strcmp(CONFIG_ETRAX_LED1G, CONFIG_ETRAX_LED1R)) - crisv32_led1_red = dummy_led; - if (!strcmp(CONFIG_ETRAX_LED2G, CONFIG_ETRAX_LED2R)) - crisv32_led2_red = dummy_led; - - return ret; -} - -__initcall(crisv32_io_init); - -int crisv32_io_get(struct crisv32_iopin* iopin, - unsigned int port, unsigned int pin) -{ - if (port > NBR_OF_PORTS) - return -EINVAL; - if (port > crisv32_ioports[port].pin_count) - return -EINVAL; - - iopin->bit = 1 << pin; - iopin->port = &crisv32_ioports[port]; - - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) - return -EIO; - - return 0; -} - -int crisv32_io_get_name(struct crisv32_iopin* iopin, - char* name) -{ - int port; - int pin; - - if (toupper(*name) == 'P') - name++; - - if (toupper(*name) < 'A' || toupper(*name) > 'E') - return -EINVAL; - - port = toupper(*name) - 'A'; - name++; - pin = simple_strtoul(name, NULL, 10); - - if (pin < 0 || pin > crisv32_ioports[port].pin_count) - return -EINVAL; - - iopin->bit = 1 << pin; - iopin->port = &crisv32_ioports[port]; - - if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) - return -EIO; - - return 0; -} - -#ifdef CONFIG_PCI -/* PCI I/O access stuff */ -struct cris_io_operations* cris_iops = NULL; -EXPORT_SYMBOL(cris_iops); -#endif - diff --git a/arch/cris/arch-v32/kernel/irq.c b/arch/cris/arch-v32/kernel/irq.c index a9acaa27024..173c141ac9b 100644 --- a/arch/cris/arch-v32/kernel/irq.c +++ b/arch/cris/arch-v32/kernel/irq.c @@ -15,15 +15,21 @@ #include <linux/threads.h> #include <linux/spinlock.h> #include <linux/kernel_stat.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/intr_vect.h> +#include <hwregs/intr_vect_defs.h> #define CPU_FIXED -1 /* IRQ masks (refer to comment for crisv32_do_multiple) */ -#define TIMER_MASK (1 << (TIMER_INTR_VECT - FIRST_IRQ)) +#if TIMER0_INTR_VECT - FIRST_IRQ < 32 +#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ)) +#undef TIMER_VECT1 +#else +#define TIMER_MASK (1 << (TIMER0_INTR_VECT - FIRST_IRQ - 32)) +#define TIMER_VECT1 +#endif #ifdef CONFIG_ETRAX_KGDB #if defined(CONFIG_ETRAX_KGDB_PORT0) #define IGNOREMASK (1 << (SER0_INTR_VECT - FIRST_IRQ)) @@ -44,8 +50,8 @@ struct cris_irq_allocation cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */ }; -struct cris_irq_allocation irq_allocations[NR_IRQS] = - {[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}}; +struct cris_irq_allocation irq_allocations[NR_REAL_IRQS] = + { [0 ... NR_REAL_IRQS - 1] = {0, CPU_MASK_ALL} }; static unsigned long irq_regs[NR_CPUS] = { @@ -55,6 +61,12 @@ static unsigned long irq_regs[NR_CPUS] = #endif }; +#if NR_REAL_IRQS > 32 +#define NBR_REGS 2 +#else +#define NBR_REGS 1 +#endif + unsigned long cpu_irq_counters[NR_CPUS]; unsigned long irq_counters[NR_REAL_IRQS]; @@ -79,45 +91,81 @@ extern void d_mmu_write(void); extern void kgdb_init(void); extern void breakpoint(void); +/* From traps.c. */ +extern void breakh_BUG(void); + /* - * Build the IRQ handler stubs using macros from irq.h. First argument is the - * IRQ number, the second argument is the corresponding bit in - * intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h. + * Build the IRQ handler stubs using macros from irq.h. */ -BUILD_IRQ(0x31, (1 << 0)) /* memarb */ -BUILD_IRQ(0x32, (1 << 1)) /* gen_io */ -BUILD_IRQ(0x33, (1 << 2)) /* iop0 */ -BUILD_IRQ(0x34, (1 << 3)) /* iop1 */ -BUILD_IRQ(0x35, (1 << 4)) /* iop2 */ -BUILD_IRQ(0x36, (1 << 5)) /* iop3 */ -BUILD_IRQ(0x37, (1 << 6)) /* dma0 */ -BUILD_IRQ(0x38, (1 << 7)) /* dma1 */ -BUILD_IRQ(0x39, (1 << 8)) /* dma2 */ -BUILD_IRQ(0x3a, (1 << 9)) /* dma3 */ -BUILD_IRQ(0x3b, (1 << 10)) /* dma4 */ -BUILD_IRQ(0x3c, (1 << 11)) /* dma5 */ -BUILD_IRQ(0x3d, (1 << 12)) /* dma6 */ -BUILD_IRQ(0x3e, (1 << 13)) /* dma7 */ -BUILD_IRQ(0x3f, (1 << 14)) /* dma8 */ -BUILD_IRQ(0x40, (1 << 15)) /* dma9 */ -BUILD_IRQ(0x41, (1 << 16)) /* ata */ -BUILD_IRQ(0x42, (1 << 17)) /* sser0 */ -BUILD_IRQ(0x43, (1 << 18)) /* sser1 */ -BUILD_IRQ(0x44, (1 << 19)) /* ser0 */ -BUILD_IRQ(0x45, (1 << 20)) /* ser1 */ -BUILD_IRQ(0x46, (1 << 21)) /* ser2 */ -BUILD_IRQ(0x47, (1 << 22)) /* ser3 */ -BUILD_IRQ(0x48, (1 << 23)) -BUILD_IRQ(0x49, (1 << 24)) /* eth0 */ -BUILD_IRQ(0x4a, (1 << 25)) /* eth1 */ -BUILD_TIMER_IRQ(0x4b, (1 << 26))/* timer */ -BUILD_IRQ(0x4c, (1 << 27)) /* bif_arb */ -BUILD_IRQ(0x4d, (1 << 28)) /* bif_dma */ -BUILD_IRQ(0x4e, (1 << 29)) /* ext */ -BUILD_IRQ(0x4f, (1 << 29)) /* ipi */ +BUILD_IRQ(0x31) +BUILD_IRQ(0x32) +BUILD_IRQ(0x33) +BUILD_IRQ(0x34) +BUILD_IRQ(0x35) +BUILD_IRQ(0x36) +BUILD_IRQ(0x37) +BUILD_IRQ(0x38) +BUILD_IRQ(0x39) +BUILD_IRQ(0x3a) +BUILD_IRQ(0x3b) +BUILD_IRQ(0x3c) +BUILD_IRQ(0x3d) +BUILD_IRQ(0x3e) +BUILD_IRQ(0x3f) +BUILD_IRQ(0x40) +BUILD_IRQ(0x41) +BUILD_IRQ(0x42) +BUILD_IRQ(0x43) +BUILD_IRQ(0x44) +BUILD_IRQ(0x45) +BUILD_IRQ(0x46) +BUILD_IRQ(0x47) +BUILD_IRQ(0x48) +BUILD_IRQ(0x49) +BUILD_IRQ(0x4a) +BUILD_IRQ(0x4b) +BUILD_IRQ(0x4c) +BUILD_IRQ(0x4d) +BUILD_IRQ(0x4e) +BUILD_IRQ(0x4f) +BUILD_IRQ(0x50) +#if MACH_IRQS > 32 +BUILD_IRQ(0x51) +BUILD_IRQ(0x52) +BUILD_IRQ(0x53) +BUILD_IRQ(0x54) +BUILD_IRQ(0x55) +BUILD_IRQ(0x56) +BUILD_IRQ(0x57) +BUILD_IRQ(0x58) +BUILD_IRQ(0x59) +BUILD_IRQ(0x5a) +BUILD_IRQ(0x5b) +BUILD_IRQ(0x5c) +BUILD_IRQ(0x5d) +BUILD_IRQ(0x5e) +BUILD_IRQ(0x5f) +BUILD_IRQ(0x60) +BUILD_IRQ(0x61) +BUILD_IRQ(0x62) +BUILD_IRQ(0x63) +BUILD_IRQ(0x64) +BUILD_IRQ(0x65) +BUILD_IRQ(0x66) +BUILD_IRQ(0x67) +BUILD_IRQ(0x68) +BUILD_IRQ(0x69) +BUILD_IRQ(0x6a) +BUILD_IRQ(0x6b) +BUILD_IRQ(0x6c) +BUILD_IRQ(0x6d) +BUILD_IRQ(0x6e) +BUILD_IRQ(0x6f) +BUILD_IRQ(0x70) +#endif /* Pointers to the low-level handlers. */ -static void (*interrupt[NR_IRQS])(void) = { +static void (*interrupt[MACH_IRQS])(void) = { IRQ0x31_interrupt, IRQ0x32_interrupt, IRQ0x33_interrupt, IRQ0x34_interrupt, IRQ0x35_interrupt, IRQ0x36_interrupt, IRQ0x37_interrupt, IRQ0x38_interrupt, IRQ0x39_interrupt, @@ -128,7 +176,20 @@ static void (*interrupt[NR_IRQS])(void) = { IRQ0x46_interrupt, IRQ0x47_interrupt, IRQ0x48_interrupt, IRQ0x49_interrupt, IRQ0x4a_interrupt, IRQ0x4b_interrupt, IRQ0x4c_interrupt, IRQ0x4d_interrupt, IRQ0x4e_interrupt, - IRQ0x4f_interrupt + IRQ0x4f_interrupt, IRQ0x50_interrupt, +#if MACH_IRQS > 32 + IRQ0x51_interrupt, IRQ0x52_interrupt, IRQ0x53_interrupt, + IRQ0x54_interrupt, IRQ0x55_interrupt, IRQ0x56_interrupt, + IRQ0x57_interrupt, IRQ0x58_interrupt, IRQ0x59_interrupt, + IRQ0x5a_interrupt, IRQ0x5b_interrupt, IRQ0x5c_interrupt, + IRQ0x5d_interrupt, IRQ0x5e_interrupt, IRQ0x5f_interrupt, + IRQ0x60_interrupt, IRQ0x61_interrupt, IRQ0x62_interrupt, + IRQ0x63_interrupt, IRQ0x64_interrupt, IRQ0x65_interrupt, + IRQ0x66_interrupt, IRQ0x67_interrupt, IRQ0x68_interrupt, + IRQ0x69_interrupt, IRQ0x6a_interrupt, IRQ0x6b_interrupt, + IRQ0x6c_interrupt, IRQ0x6d_interrupt, IRQ0x6e_interrupt, + IRQ0x6f_interrupt, IRQ0x70_interrupt, +#endif }; void @@ -137,13 +198,26 @@ block_irq(int irq, int cpu) int intr_mask; unsigned long flags; - spin_lock_irqsave(&irq_lock, flags); - intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - - /* Remember; 1 let through, 0 block. */ - intr_mask &= ~(1 << (irq - FIRST_IRQ)); - - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); + spin_lock_irqsave(&irq_lock, flags); + if (irq - FIRST_IRQ < 32) + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 0); + else + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 1); + + /* Remember; 1 let thru, 0 block. */ + if (irq - FIRST_IRQ < 32) + intr_mask &= ~(1 << (irq - FIRST_IRQ)); + else + intr_mask &= ~(1 << (irq - FIRST_IRQ - 32)); + + if (irq - FIRST_IRQ < 32) + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 0, intr_mask); + else + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 1, intr_mask); spin_unlock_irqrestore(&irq_lock, flags); } @@ -154,12 +228,26 @@ unblock_irq(int irq, int cpu) unsigned long flags; spin_lock_irqsave(&irq_lock, flags); - intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - - /* Remember; 1 let through, 0 block. */ - intr_mask |= (1 << (irq - FIRST_IRQ)); + if (irq - FIRST_IRQ < 32) + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 0); + else + intr_mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + rw_mask, 1); + + /* Remember; 1 let thru, 0 block. */ + if (irq - FIRST_IRQ < 32) + intr_mask |= (1 << (irq - FIRST_IRQ)); + else + intr_mask |= (1 << (irq - FIRST_IRQ - 32)); + + if (irq - FIRST_IRQ < 32) + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 0, intr_mask); + else + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, + 1, intr_mask); - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, intr_mask); spin_unlock_irqrestore(&irq_lock, flags); } @@ -298,8 +386,9 @@ crisv32_do_multiple(struct pt_regs* regs) { int cpu; int mask; - int masked; + int masked[NBR_REGS]; int bit; + int i; cpu = smp_processor_id(); @@ -308,42 +397,59 @@ crisv32_do_multiple(struct pt_regs* regs) */ irq_enter(); - /* Get which IRQs that happened. */ - masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect); + for (i = 0; i < NBR_REGS; i++) { + /* Get which IRQs that happend. */ + masked[i] = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], + r_masked_vect, i); - /* Calculate new IRQ mask with these IRQs disabled. */ - mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - mask &= ~masked; + /* Calculate new IRQ mask with these IRQs disabled. */ + mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i); + mask &= ~masked[i]; /* Timer IRQ is never masked */ - if (masked & TIMER_MASK) - mask |= TIMER_MASK; - - /* Block all the IRQs */ - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); +#ifdef TIMER_VECT1 + if ((i == 1) && (masked[0] & TIMER_MASK)) + mask |= TIMER_MASK; +#else + if ((i == 0) && (masked[0] & TIMER_MASK)) + mask |= TIMER_MASK; +#endif + /* Block all the IRQs */ + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask); /* Check for timer IRQ and handle it special. */ - if (masked & TIMER_MASK) { - masked &= ~TIMER_MASK; - do_IRQ(TIMER_INTR_VECT, regs); +#ifdef TIMER_VECT1 + if ((i == 1) && (masked[i] & TIMER_MASK)) { + masked[i] &= ~TIMER_MASK; + do_IRQ(TIMER0_INTR_VECT, regs); + } +#else + if ((i == 0) && (masked[i] & TIMER_MASK)) { + masked[i] &= ~TIMER_MASK; + do_IRQ(TIMER0_INTR_VECT, regs); + } } +#endif #ifdef IGNORE_MASK /* Remove IRQs that can't be handled as multiple. */ - masked &= ~IGNORE_MASK; + masked[0] &= ~IGNORE_MASK; #endif /* Handle the rest of the IRQs. */ - for (bit = 0; bit < 32; bit++) - { - if (masked & (1 << bit)) - do_IRQ(bit + FIRST_IRQ, regs); + for (i = 0; i < NBR_REGS; i++) { + for (bit = 0; bit < 32; bit++) { + if (masked[i] & (1 << bit)) + do_IRQ(bit + FIRST_IRQ + i*32, regs); + } } /* Unblock all the IRQs. */ - mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask); - mask |= masked; - REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask); + for (i = 0; i < NBR_REGS; i++) { + mask = REG_RD_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i); + mask |= masked[i]; + REG_WR_INT_VECT(intr_vect, irq_regs[cpu], rw_mask, i, mask); + } /* This irq_exit() will trigger the soft IRQs. */ irq_exit(); @@ -361,20 +467,21 @@ init_IRQ(void) reg_intr_vect_rw_mask vect_mask = {0}; /* Clear all interrupts masks. */ - REG_WR(intr_vect, regi_irq, rw_mask, vect_mask); + for (i = 0; i < NBR_REGS; i++) + REG_WR_VECT(intr_vect, regi_irq, rw_mask, i, vect_mask); for (i = 0; i < 256; i++) etrax_irv->v[i] = weird_irq; - /* Point all IRQs to bad handlers. */ + /* Point all IRQ's to bad handlers. */ for (i = FIRST_IRQ, j = 0; j < NR_IRQS; i++, j++) { irq_desc[j].chip = &crisv32_irq_type; set_exception_vector(i, interrupt[j]); } /* Mark Timer and IPI IRQs as CPU local */ - irq_allocations[TIMER_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; - irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU; + irq_allocations[TIMER0_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; + irq_desc[TIMER0_INTR_VECT].status |= IRQ_PER_CPU; irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED; irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU; @@ -391,6 +498,11 @@ init_IRQ(void) set_exception_vector(0x0a, d_mmu_access); set_exception_vector(0x0b, d_mmu_write); +#ifdef CONFIG_BUG + /* Break 14 handler, used to implement cheap BUG(). */ + set_exception_vector(0x1e, breakh_BUG); +#endif + /* The system-call trap is reached by "break 13". */ set_exception_vector(0x1d, system_call); diff --git a/arch/cris/arch-v32/kernel/kgdb.c b/arch/cris/arch-v32/kernel/kgdb.c index 480e56348be..4e2e2e271ef 100644 --- a/arch/cris/arch-v32/kernel/kgdb.c +++ b/arch/cris/arch-v32/kernel/kgdb.c @@ -381,7 +381,7 @@ static int read_register(char regno, unsigned int *valptr); /* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ int getDebugChar(void); -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM int getDebugChar(void) { return socketread(); @@ -391,7 +391,7 @@ int getDebugChar(void) /* Serial port, writes one character. ETRAX 100 specific. from debugport.c */ void putDebugChar(int val); -#ifdef CONFIG_ETRAXFS_SIM +#ifdef CONFIG_ETRAX_VCS_SIM void putDebugChar(int val) { socketwrite((char *)&val, 1); @@ -1599,7 +1599,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser0, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser0, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT1) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1611,7 +1611,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser1, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser1, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT2) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1623,7 +1623,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser2, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser2, rw_intr_mask, ser_intr_mask); #elif defined(CONFIG_ETRAX_KGDB_PORT3) /* Note: no shortcut registered (not handled by multiple_interrupt). @@ -1635,7 +1635,7 @@ kgdb_init(void) REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); ser_intr_mask = REG_RD(ser, regi_ser3, rw_intr_mask); - ser_intr_mask.data_avail = regk_ser_yes; + ser_intr_mask.dav = regk_ser_yes; REG_WR(ser, regi_ser3, rw_intr_mask, ser_intr_mask); #endif diff --git a/arch/cris/arch-v32/kernel/process.c b/arch/cris/arch-v32/kernel/process.c index b72a15580dc..ced5b725d9b 100644 --- a/arch/cris/arch-v32/kernel/process.c +++ b/arch/cris/arch-v32/kernel/process.c @@ -12,17 +12,13 @@ #include <linux/err.h> #include <linux/fs.h> #include <linux/slab.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/timer_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/reg_map.h> +#include <hwregs/timer_defs.h> +#include <hwregs/intr_vect_defs.h> extern void stop_watchdog(void); -#ifdef CONFIG_ETRAX_GPIO -extern void etrax_gpio_wake_up_check(void); /* Defined in drivers/gpio.c. */ -#endif - extern int cris_hlt_counter; /* We use this if we don't have any better idle routine. */ @@ -82,7 +78,7 @@ hard_reset_now(void) wd_ctrl.cmd = regk_timer_start; arch_enable_nmi(); - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); } #endif diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c index 2df60529a8a..e27f4670e88 100644 --- a/arch/cris/arch-v32/kernel/ptrace.c +++ b/arch/cris/arch-v32/kernel/ptrace.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2000-2003, Axis Communications AB. + * Copyright (C) 2000-2007, Axis Communications AB. */ #include <linux/kernel.h> @@ -149,7 +149,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = generic_ptrace_pokedata(child, addr, data); break; - /* Write the word at location address in the USER area. */ + /* Write the word at location address in the USER area. */ case PTRACE_POKEUSR: ret = -EIO; if ((addr & 3) || addr < 0 || addr > PT_MAX << 2) @@ -201,7 +201,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; - /* Make the child exit by sending it a sigkill. */ + /* Make the child exit by sending it a sigkill. */ case PTRACE_KILL: ret = 0; @@ -245,9 +245,10 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } + /* Get all GP registers from the child. */ case PTRACE_GETREGS: { - int i; + int i; unsigned long tmp; for (i = 0; i <= PT_MAX; i++) { @@ -294,6 +295,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } +out_tsk: return ret; } diff --git a/arch/cris/arch-v32/kernel/signal.c b/arch/cris/arch-v32/kernel/signal.c index 024cc690197..58c1866804e 100644 --- a/arch/cris/arch-v32/kernel/signal.c +++ b/arch/cris/arch-v32/kernel/signal.c @@ -50,7 +50,7 @@ struct rt_signal_frame { unsigned char retcode[8]; /* Trampoline code. */ }; -int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs); +void do_signal(int restart, struct pt_regs *regs); void keep_debug_flags(unsigned long oldccs, unsigned long oldspc, struct pt_regs *regs); /* @@ -61,74 +61,16 @@ int sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, long srp, struct pt_regs *regs) { - sigset_t saveset; - mask &= _BLOCKABLE; - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - + current->saved_sigmask = current->blocked; siginitset(¤t->blocked, mask); - - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* - * This point is reached twice: once to call - * the signal handler, then again to return - * from the sigsuspend system call. When - * calling the signal handler, R10 hold the - * signal number as set by do_signal(). The - * sigsuspend call will always return with - * the restored value above; -EINTR. - */ - return regs->r10; - } - } -} - -/* Define some dummy arguments to be able to reach the regs argument. */ -int -sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, - long mof, long srp, struct pt_regs *regs) -{ - sigset_t saveset; - sigset_t newset; - - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - - sigdelsetmask(&newset, ~_BLOCKABLE); - spin_lock_irq(¤t->sighand->siglock); - - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - - regs->r10 = -EINTR; - - while (1) { - current->state = TASK_INTERRUPTIBLE; - schedule(); - - if (do_signal(0, &saveset, regs)) { - /* See comment in function above. */ - return regs->r10; - } - } + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; } int @@ -290,7 +232,7 @@ sys_rt_sigreturn(long r10, long r11, long r12, long r13, long mof, long srp, goto badframe; if (do_sigaltstack(&frame->uc.uc_stack, NULL, rdusp()) == -EFAULT) - goto badframe; + goto badframe; keep_debug_flags(oldccs, oldspc, regs); @@ -347,11 +289,11 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) /* Grab and setup a signal frame. * * Basically a lot of state-info is stacked, and arranged for the - * user-mode program to return to the kernel using either a trampoline + * user-mode program to return to the kernel using either a trampiline * which performs the syscall sigreturn(), or a provided user-mode * trampoline. */ -static void +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { @@ -417,16 +359,17 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, /* Actually move the USP to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; force_sig(SIGSEGV, current); + return -EFAULT; } -static void +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { @@ -503,21 +446,24 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Actually move the usp to reflect the stacked frame. */ wrusp((unsigned long)frame); - return; + return 0; give_sigsegv: if (sig == SIGSEGV) ka->sa.sa_handler = SIG_DFL; force_sig(SIGSEGV, current); + return -EFAULT; } /* Invoke a singal handler to, well, handle the signal. */ -static inline void +static inline int handle_signal(int canrestart, unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs * regs) { + int ret; + /* Check if this got called from a system call. */ if (canrestart) { /* If so, check system call restarting. */ @@ -561,19 +507,24 @@ handle_signal(int canrestart, unsigned long sig, /* Set up the stack frame. */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked,sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; } /* @@ -587,12 +538,13 @@ handle_signal(int canrestart, unsigned long sig, * we can use user_mode(regs) to see if we came directly from kernel or user * mode below. */ -int -do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +void +do_signal(int canrestart, struct pt_regs *regs) { int signr; siginfo_t info; struct k_sigaction ka; + sigset_t *oldset; /* * The common case should go fast, which is why this point is @@ -600,17 +552,28 @@ do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) * without doing anything. */ if (!user_mode(regs)) - return 1; + return; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - /* Deliver the signal. */ - handle_signal(canrestart, signr, &info, &ka, oldset, regs); - return 1; + /* Whee! Actually deliver the signal. */ + if (handle_signal(canrestart, signr, &info, &ka, + oldset, regs)) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return; } /* Got here from a system call? */ @@ -628,7 +591,12 @@ do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) } } - return 0; + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } } asmlinkage void @@ -641,7 +609,7 @@ ugdb_trap_user(struct thread_info *ti, int sig) user_regs(ti)->spc = 0; } /* FIXME: Filter out false h/w breakpoint hits (i.e. EDA - not within any configured h/w breakpoint range). Synchronize with + not withing any configured h/w breakpoint range). Synchronize with what already exists for kernel debugging. */ if (((user_regs(ti)->exs & 0xff00) >> 8) == BREAK_8_INTR_VECT) { /* Break 8: subtract 2 from ERP unless in a delay slot. */ diff --git a/arch/cris/arch-v32/kernel/smp.c b/arch/cris/arch-v32/kernel/smp.c index 171c96e0a5d..a9c3334e46c 100644 --- a/arch/cris/arch-v32/kernel/smp.c +++ b/arch/cris/arch-v32/kernel/smp.c @@ -1,11 +1,12 @@ +#include <linux/types.h> #include <asm/delay.h> -#include <asm/arch/irq.h> -#include <asm/arch/hwregs/intr_vect.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <irq.h> +#include <hwregs/intr_vect.h> +#include <hwregs/intr_vect_defs.h> #include <asm/tlbflush.h> #include <asm/mmu_context.h> -#include <asm/arch/hwregs/mmu_defs_asm.h> -#include <asm/arch/hwregs/supp_reg.h> +#include <hwregs/asm/mmu_defs_asm.h> +#include <hwregs/supp_reg.h> #include <asm/atomic.h> #include <linux/err.h> @@ -20,6 +21,7 @@ #define IPI_SCHEDULE 1 #define IPI_CALL 2 #define IPI_FLUSH_TLB 4 +#define IPI_BOOT 8 #define FLUSH_ALL (void*)0xffffffff @@ -30,6 +32,8 @@ spinlock_t cris_atomic_locks[] = { [0 ... LOCK_COUNT - 1] = SPIN_LOCK_UNLOCKED}; cpumask_t cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); cpumask_t phys_cpu_present_map = CPU_MASK_NONE; +cpumask_t cpu_possible_map; +EXPORT_SYMBOL(cpu_possible_map); EXPORT_SYMBOL(phys_cpu_present_map); /* Variables used during SMP boot */ @@ -55,13 +59,12 @@ static unsigned long flush_addr; extern int setup_irq(int, struct irqaction *); /* Mode registers */ -static unsigned long irq_regs[NR_CPUS] = -{ +static unsigned long irq_regs[NR_CPUS] = { regi_irq, regi_irq2 }; -static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id); static int send_ipi(int vector, int wait, cpumask_t cpu_mask); static struct irqaction irq_ipi = { .handler = crisv32_ipi_interrupt, @@ -101,6 +104,7 @@ void __devinit smp_prepare_boot_cpu(void) cpu_set(0, cpu_online_map); cpu_set(0, phys_cpu_present_map); + cpu_set(0, cpu_possible_map); } void __init smp_cpus_done(unsigned int max_cpus) @@ -113,6 +117,7 @@ smp_boot_one_cpu(int cpuid) { unsigned timeout; struct task_struct *idle; + cpumask_t cpu_mask = CPU_MASK_NONE; idle = fork_idle(cpuid); if (IS_ERR(idle)) @@ -124,6 +129,12 @@ smp_boot_one_cpu(int cpuid) smp_init_current_idle_thread = task_thread_info(idle); cpu_now_booting = cpuid; + /* Kick it */ + cpu_set(cpuid, cpu_online_map); + cpu_set(cpuid, cpu_mask); + send_ipi(IPI_BOOT, 0, cpu_mask); + cpu_clear(cpuid, cpu_online_map); + /* Wait for CPU to come online */ for (timeout = 0; timeout < 10000; timeout++) { if(cpu_online(cpuid)) { @@ -165,7 +176,7 @@ void __init smp_callin(void) /* Enable IRQ and idle */ REG_WR(intr_vect, irq_regs[cpu], rw_mask, vect_mask); unmask_irq(IPI_INTR_VECT); - unmask_irq(TIMER_INTR_VECT); + unmask_irq(TIMER0_INTR_VECT); preempt_disable(); local_irq_enable(); @@ -328,7 +339,7 @@ int smp_call_function(void (*func)(void *info), void *info, return ret; } -irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t crisv32_ipi_interrupt(int irq, void *dev_id) { void (*func) (void *info) = call_data->func; void *info = call_data->info; diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c index 2f7e8e200f2..3a13dd6e0a9 100644 --- a/arch/cris/arch-v32/kernel/time.c +++ b/arch/cris/arch-v32/kernel/time.c @@ -1,8 +1,7 @@ -/* $Id: time.c,v 1.19 2005/04/29 05:40:09 starvik Exp $ - * +/* * linux/arch/cris/arch-v32/kernel/time.c * - * Copyright (C) 2003 Axis Communications AB + * Copyright (C) 2003-2007 Axis Communications AB * */ @@ -14,28 +13,34 @@ #include <linux/sched.h> #include <linux/init.h> #include <linux/threads.h> +#include <linux/cpufreq.h> #include <asm/types.h> #include <asm/signal.h> #include <asm/io.h> #include <asm/delay.h> #include <asm/rtc.h> #include <asm/irq.h> - -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/timer_defs.h> -#include <asm/arch/hwregs/intr_vect_defs.h> +#include <asm/irq_regs.h> + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> +#include <hwregs/intr_vect_defs.h> +#ifdef CONFIG_CRIS_MACH_ARTPEC3 +#include <hwregs/clkgen_defs.h> +#endif /* Watchdog defines */ -#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */ -#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ -#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) /* Number of 763 counts before watchdog bites */ +#define ETRAX_WD_KEY_MASK 0x7F /* key is 7 bit */ +#define ETRAX_WD_HZ 763 /* watchdog counts at 763 Hz */ +/* Number of 763 counts before watchdog bites */ +#define ETRAX_WD_CNT ((2*ETRAX_WD_HZ)/HZ + 1) unsigned long timer_regs[NR_CPUS] = { - regi_timer, + regi_timer0, #ifdef CONFIG_SMP - regi_timer2 + regi_timer2 #endif }; @@ -44,12 +49,22 @@ extern int set_rtc_mmss(unsigned long nowtime); extern int setup_irq(int, struct irqaction *); extern int have_rtc; +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_time_freq_notifier_block = { + .notifier_call = cris_time_freq_notifier, +}; +#endif + unsigned long get_ns_in_jiffie(void) { reg_timer_r_tmr0_data data; unsigned long ns; - data = REG_RD(timer, regi_timer, r_tmr0_data); + data = REG_RD(timer, regi_timer0, r_tmr0_data); ns = (TIMER0_DIV - data) * 10; return ns; } @@ -59,31 +74,27 @@ unsigned long do_slow_gettimeoffset(void) unsigned long count; unsigned long usec_count = 0; - static unsigned long count_p = TIMER0_DIV;/* for the first call after boot */ + /* For the first call after boot */ + static unsigned long count_p = TIMER0_DIV; static unsigned long jiffies_p = 0; - /* - * cache volatile jiffies temporarily; we have IRQs turned off. - */ + /* Cache volatile jiffies temporarily; we have IRQs turned off. */ unsigned long jiffies_t; /* The timer interrupt comes from Etrax timer 0. In order to get * better precision, we check the current value. It might have - * underflowed already though. - */ + * underflowed already though. */ + count = REG_RD(timer, regi_timer0, r_tmr0_data); + jiffies_t = jiffies; - count = REG_RD(timer, regi_timer, r_tmr0_data); - jiffies_t = jiffies; - - /* - * avoiding timer inconsistencies (they are rare, but they happen)... - * there are one problem that must be avoided here: - * 1. the timer counter underflows + /* Avoiding timer inconsistencies (they are rare, but they happen) + * There is one problem that must be avoided here: + * 1. the timer counter underflows */ if( jiffies_t == jiffies_p ) { if( count > count_p ) { - /* Timer wrapped, use new count and prescale - * increase the time corresponding to one jiffie + /* Timer wrapped, use new count and prescale. + * Increase the time corresponding to one jiffy. */ usec_count = 1000000/HZ; } @@ -106,17 +117,15 @@ unsigned long do_slow_gettimeoffset(void) */ /* This gives us 1.3 ms to do something useful when the NMI comes */ -/* right now, starting the watchdog is the same as resetting it */ +/* Right now, starting the watchdog is the same as resetting it */ #define start_watchdog reset_watchdog #if defined(CONFIG_ETRAX_WATCHDOG) static short int watchdog_key = 42; /* arbitrary 7 bit number */ #endif -/* number of pages to consider "out of memory". it is normal that the memory - * is used though, so put this really low. - */ - +/* Number of pages to consider "out of memory". It is normal that the memory + * is used though, so set this really low. */ #define WATCHDOG_MIN_FREE_PAGES 8 void @@ -125,14 +134,15 @@ reset_watchdog(void) #if defined(CONFIG_ETRAX_WATCHDOG) reg_timer_rw_wd_ctrl wd_ctrl = { 0 }; - /* only keep watchdog happy as long as we have memory left! */ + /* Only keep watchdog happy as long as we have memory left! */ if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) { - /* reset the watchdog with the inverse of the old key */ - watchdog_key ^= ETRAX_WD_KEY_MASK; /* invert key, which is 7 bits */ + /* Reset the watchdog with the inverse of the old key */ + /* Invert key, which is 7 bits */ + watchdog_key ^= ETRAX_WD_KEY_MASK; wd_ctrl.cnt = ETRAX_WD_CNT; wd_ctrl.cmd = regk_timer_start; wd_ctrl.key = watchdog_key; - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); } #endif } @@ -148,7 +158,7 @@ stop_watchdog(void) wd_ctrl.cnt = ETRAX_WD_CNT; wd_ctrl.cmd = regk_timer_stop; wd_ctrl.key = watchdog_key; - REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl); + REG_WR(timer, regi_timer0, rw_wd_ctrl, wd_ctrl); #endif } @@ -160,17 +170,28 @@ handle_watchdog_bite(struct pt_regs* regs) #if defined(CONFIG_ETRAX_WATCHDOG) extern int cause_of_death; - raw_printk("Watchdog bite\n"); + oops_in_progress = 1; + printk(KERN_WARNING "Watchdog bite\n"); /* Check if forced restart or unexpected watchdog */ if (cause_of_death == 0xbedead) { +#ifdef CONFIG_CRIS_MACH_ARTPEC3 + /* There is a bug in Artpec-3 (voodoo TR 78) that requires + * us to go to lower frequency for the reset to be reliable + */ + reg_clkgen_rw_clk_ctrl ctrl = + REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + ctrl.pll = 0; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, ctrl); +#endif while(1); } - /* Unexpected watchdog, stop the watchdog and dump registers*/ + /* Unexpected watchdog, stop the watchdog and dump registers. */ stop_watchdog(); - raw_printk("Oops: bitten by watchdog\n"); - show_registers(regs); + printk(KERN_WARNING "Oops: bitten by watchdog\n"); + show_registers(regs); + oops_in_progress = 0; #ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY reset_watchdog(); #endif @@ -178,21 +199,19 @@ handle_watchdog_bite(struct pt_regs* regs) #endif } -/* last time the cmos clock got updated */ +/* Last time the cmos clock got updated. */ static long last_rtc_update = 0; /* * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick + * as well as call the "do_timer()" routine every clocktick. */ - -//static unsigned short myjiff; /* used by our debug routine print_timestamp */ - extern void cris_do_profile(struct pt_regs *regs); static inline irqreturn_t -timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +timer_interrupt(int irq, void *dev_id) { + struct pt_regs *regs = get_irq_regs(); int cpu = smp_processor_id(); reg_timer_r_masked_intr masked_intr; reg_timer_rw_ack_intr ack_intr = { 0 }; @@ -202,11 +221,11 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (!masked_intr.tmr0) return IRQ_NONE; - /* acknowledge the timer irq */ + /* Acknowledge the timer irq. */ ack_intr.tmr0 = 1; REG_WR(timer, timer_regs[cpu], rw_ack_intr, ack_intr); - /* reset watchdog otherwise it resets us! */ + /* Reset watchdog otherwise it resets us! */ reset_watchdog(); /* Update statistics. */ @@ -218,7 +237,7 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (cpu != 0) return IRQ_HANDLED; - /* call the real timer interrupt handler */ + /* Call the real timer interrupt handler */ do_timer(1); /* @@ -236,17 +255,17 @@ timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (set_rtc_mmss(xtime.tv_sec) == 0) last_rtc_update = xtime.tv_sec; else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + /* Do it again in 60 s */ + last_rtc_update = xtime.tv_sec - 600; } return IRQ_HANDLED; } -/* timer is IRQF_SHARED so drivers can add stuff to the timer irq chain - * it needs to be IRQF_DISABLED to make the jiffies update work properly +/* Timer is IRQF_SHARED so drivers can add stuff to the timer irq chain. + * It needs to be IRQF_DISABLED to make the jiffies update work properly. */ - -static struct irqaction irq_timer = { - .mask = timer_interrupt, +static struct irqaction irq_timer = { + .handler = timer_interrupt, .flags = IRQF_SHARED | IRQF_DISABLED, .mask = CPU_MASK_NONE, .name = "timer" @@ -256,27 +275,27 @@ void __init cris_timer_init(void) { int cpu = smp_processor_id(); - reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; - reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV; + reg_timer_rw_tmr0_ctrl tmr0_ctrl = { 0 }; + reg_timer_rw_tmr0_div tmr0_div = TIMER0_DIV; reg_timer_rw_intr_mask timer_intr_mask; - /* Setup the etrax timers + /* Setup the etrax timers. * Base frequency is 100MHz, divider 1000000 -> 100 HZ * We use timer0, so timer1 is free. * The trig timer is used by the fasttimer API if enabled. */ - tmr0_ctrl.op = regk_timer_ld; + tmr0_ctrl.op = regk_timer_ld; tmr0_ctrl.freq = regk_timer_f100; - REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div); - REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */ - tmr0_ctrl.op = regk_timer_run; - REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */ - - /* enable the timer irq */ - timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask); - timer_intr_mask.tmr0 = 1; - REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); + REG_WR(timer, timer_regs[cpu], rw_tmr0_div, tmr0_div); + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Load */ + tmr0_ctrl.op = regk_timer_run; + REG_WR(timer, timer_regs[cpu], rw_tmr0_ctrl, tmr0_ctrl); /* Start */ + + /* Enable the timer irq. */ + timer_intr_mask = REG_RD(timer, timer_regs[cpu], rw_intr_mask); + timer_intr_mask.tmr0 = 1; + REG_WR(timer, timer_regs[cpu], rw_intr_mask, timer_intr_mask); } void __init @@ -284,7 +303,7 @@ time_init(void) { reg_intr_vect_rw_mask intr_mask; - /* probe for the RTC and read it if it exists + /* Probe for the RTC and read it if it exists. * Before the RTC can be probed the loops_per_usec variable needs * to be initialized to make usleep work. A better value for * loops_per_usec is calculated by the kernel later once the @@ -293,52 +312,74 @@ time_init(void) loops_per_usec = 50; if(RTC_INIT() < 0) { - /* no RTC, start at 1980 */ + /* No RTC, start at 1980 */ xtime.tv_sec = 0; xtime.tv_nsec = 0; have_rtc = 0; } else { - /* get the current time */ + /* Get the current time */ have_rtc = 1; update_xtime_from_cmos(); } /* - * Initialize wall_to_monotonic such that adding it to xtime will yield zero, the - * tv_nsec field must be normalized (i.e., 0 <= nsec < NSEC_PER_SEC). + * Initialize wall_to_monotonic such that adding it to + * xtime will yield zero, the tv_nsec field must be normalized + * (i.e., 0 <= nsec < NSEC_PER_SEC). */ set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec); - /* Start CPU local timer */ + /* Start CPU local timer. */ cris_timer_init(); - /* enable the timer irq in global config */ - intr_mask = REG_RD(intr_vect, regi_irq, rw_mask); - intr_mask.timer = 1; - REG_WR(intr_vect, regi_irq, rw_mask, intr_mask); - - /* now actually register the timer irq handler that calls timer_interrupt() */ + /* Enable the timer irq in global config. */ + intr_mask = REG_RD_VECT(intr_vect, regi_irq, rw_mask, 1); + intr_mask.timer0 = 1; + REG_WR_VECT(intr_vect, regi_irq, rw_mask, 1, intr_mask); - setup_irq(TIMER_INTR_VECT, &irq_timer); + /* Now actually register the timer irq handler that calls + * timer_interrupt(). */ + setup_irq(TIMER0_INTR_VECT, &irq_timer); - /* enable watchdog if we should use one */ + /* Enable watchdog if we should use one. */ #if defined(CONFIG_ETRAX_WATCHDOG) - printk("Enabling watchdog...\n"); + printk(KERN_INFO "Enabling watchdog...\n"); start_watchdog(); /* If we use the hardware watchdog, we want to trap it as an NMI - and dump registers before it resets us. For this to happen, we - must set the "m" NMI enable flag (which once set, is unset only - when an NMI is taken). - - The same goes for the external NMI, but that doesn't have any - driver or infrastructure support yet. */ - { - unsigned long flags; - local_save_flags(flags); - flags |= (1<<30); /* NMI M flag is at bit 30 */ - local_irq_restore(flags); - } + * and dump registers before it resets us. For this to happen, we + * must set the "m" NMI enable flag (which once set, is unset only + * when an NMI is taken). */ + { + unsigned long flags; + local_save_flags(flags); + flags |= (1<<30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); + } +#endif + +#ifdef CONFIG_CPU_FREQ + cpufreq_register_notifier(&cris_time_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); #endif } + +#ifdef CONFIG_CPU_FREQ +static int +cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_POSTCHANGE) { + reg_timer_r_tmr0_data data; + reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ; + do { + data = REG_RD(timer, timer_regs[freqs->cpu], + r_tmr0_data); + } while (data > 20); + REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div); + } + return 0; +} +#endif diff --git a/arch/cris/arch-v32/kernel/traps.c b/arch/cris/arch-v32/kernel/traps.c index 17fd3dbd1c8..9003e382cad 100644 --- a/arch/cris/arch-v32/kernel/traps.c +++ b/arch/cris/arch-v32/kernel/traps.c @@ -1,50 +1,45 @@ /* - * Copyright (C) 2003, Axis Communications AB. + * Copyright (C) 2003-2006, Axis Communications AB. */ #include <linux/ptrace.h> +#include <linux/module.h> #include <asm/uaccess.h> - -#include <asm/arch/hwregs/supp_reg.h> - -extern void reset_watchdog(void); -extern void stop_watchdog(void); - -extern int raw_printk(const char *fmt, ...); +#include <hwregs/supp_reg.h> +#include <hwregs/intr_vect_defs.h> +#include <asm/irq.h> void show_registers(struct pt_regs *regs) { /* * It's possible to use either the USP register or current->thread.usp. - * USP might not correspond to the current proccess for all cases this + * USP might not correspond to the current process for all cases this * function is called, and current->thread.usp isn't up to date for the - * current proccess. Experience shows that using USP is the way to go. + * current process. Experience shows that using USP is the way to go. */ - unsigned long usp; + unsigned long usp = rdusp(); unsigned long d_mmu_cause; unsigned long i_mmu_cause; - usp = rdusp(); + printk("CPU: %d\n", smp_processor_id()); - raw_printk("CPU: %d\n", smp_processor_id()); + printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", + regs->erp, regs->srp, regs->ccs, usp, regs->mof); - raw_printk("ERP: %08lx SRP: %08lx CCS: %08lx USP: %08lx MOF: %08lx\n", - regs->erp, regs->srp, regs->ccs, usp, regs->mof); + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); - raw_printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", - regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); - raw_printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", - regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); - raw_printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", - regs->r8, regs->r9, regs->r10, regs->r11); + printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", + regs->r12, regs->r13, regs->orig_r10, regs->acr); - raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n", - regs->r12, regs->r13, regs->orig_r10, regs->acr); - - raw_printk("sp: %08lx\n", regs); + printk(" sp: %08lx\n", (unsigned long)regs); SUPP_BANK_SEL(BANK_IM); SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause); @@ -52,18 +47,20 @@ show_registers(struct pt_regs *regs) SUPP_BANK_SEL(BANK_DM); SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause); - raw_printk(" Data MMU Cause: %08lx\n", d_mmu_cause); - raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); + printk(" Data MMU Cause: %08lx\n", d_mmu_cause); + printk("Instruction MMU Cause: %08lx\n", i_mmu_cause); - raw_printk("Process %s (pid: %d, stackpage: %08lx)\n", - current->comm, current->pid, (unsigned long) current); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, (unsigned long)current); - /* Show additional info if in kernel-mode. */ + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ if (!user_mode(regs)) { int i; - unsigned char c; - show_stack(NULL, (unsigned long *) usp); + show_stack(NULL, (unsigned long *)usp); /* * If the previous stack-dump wasn't a kernel one, dump the @@ -72,7 +69,7 @@ show_registers(struct pt_regs *regs) if (usp != 0) show_stack(NULL, NULL); - raw_printk("\nCode: "); + printk("\nCode: "); if (regs->erp < PAGE_OFFSET) goto bad_value; @@ -84,76 +81,115 @@ show_registers(struct pt_regs *regs) * instruction decoding should be in sync at the interesting * point, but small enough to fit on a row. The regs->erp * location is pointed out in a ksymoops-friendly way by - * wrapping the byte for that address in parenthesis. + * wrapping the byte for that address in parenthesises. */ for (i = -12; i < 12; i++) { - if (__get_user(c, &((unsigned char *) regs->erp)[i])) { + unsigned char c; + + if (__get_user(c, &((unsigned char *)regs->erp)[i])) { bad_value: - raw_printk(" Bad IP value."); + printk(" Bad IP value."); break; } if (i == 0) - raw_printk("(%02x) ", c); + printk("(%02x) ", c); else - raw_printk("%02x ", c); + printk("%02x ", c); } - - raw_printk("\n"); + printk("\n"); } } -/* - * This gets called from entry.S when the watchdog has bitten. Show something - * similar to an Oops dump, and if the kernel is configured to be a nice doggy; - * halt instead of reboot. - */ void -watchdog_bite_hook(struct pt_regs *regs) +arch_enable_nmi(void) { -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - local_irq_disable(); - stop_watchdog(); - show_registers(regs); + unsigned long flags; - while (1) - ; /* Do nothing. */ -#else - show_registers(regs); -#endif + local_save_flags(flags); + flags |= (1 << 30); /* NMI M flag is at bit 30 */ + local_irq_restore(flags); } -/* This is normally the Oops function. */ -void -die_if_kernel(const char *str, struct pt_regs *regs, long err) +extern void (*nmi_handler)(struct pt_regs *); +void handle_nmi(struct pt_regs *regs) { - if (user_mode(regs)) - return; +#ifdef CONFIG_ETRAXFS + reg_intr_vect_r_nmi r; +#endif -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - /* - * This printout might take too long and could trigger - * the watchdog normally. If NICE_DOGGY is set, simply - * stop the watchdog during the printout. - */ - stop_watchdog(); + if (nmi_handler) + nmi_handler(regs); + +#ifdef CONFIG_ETRAXFS + /* Wait until nmi is no longer active. */ + do { + r = REG_RD(intr_vect, regi_irq, r_nmi); + } while (r.ext == regk_intr_vect_on); #endif +} - raw_printk("%s: %04lx\n", str, err & 0xffff); - show_registers(regs); +#ifdef CONFIG_BUG +extern void die_if_kernel(const char *str, struct pt_regs *regs, long err); -#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY - reset_watchdog(); -#endif +/* Copy of the regs at BUG() time. */ +struct pt_regs BUG_regs; - do_exit(SIGSEGV); +void do_BUG(char *file, unsigned int line) +{ + printk("kernel BUG at %s:%d!\n", file, line); + die_if_kernel("Oops", &BUG_regs, 0); } +EXPORT_SYMBOL(do_BUG); -void arch_enable_nmi(void) +void fixup_BUG(struct pt_regs *regs) { - unsigned long flags; - local_save_flags(flags); - flags |= (1<<30); /* NMI M flag is at bit 30 */ - local_irq_restore(flags); + BUG_regs = *regs; + +#ifdef CONFIG_DEBUG_BUGVERBOSE + /* + * Fixup the BUG arguments through exception handlers. + */ + { + const struct exception_table_entry *fixup; + + /* + * ERP points at the "break 14" + 2, compensate for the 2 + * bytes. + */ + fixup = search_exception_tables(instruction_pointer(regs) - 2); + if (fixup) { + /* Adjust the instruction pointer in the stackframe. */ + instruction_pointer(regs) = fixup->fixup; + arch_fixup(regs); + } + } +#else + /* Dont try to lookup the filename + line, just dump regs. */ + do_BUG("unknown", 0); +#endif } + +/* + * Break 14 handler. Save regs and jump into the fixup_BUG. + */ +__asm__ ( ".text\n\t" + ".global breakh_BUG\n\t" + "breakh_BUG:\n\t" + SAVE_ALL + KGDB_FIXUP + "move.d $sp, $r10\n\t" + "jsr fixup_BUG\n\t" + "nop\n\t" + "jump ret_from_intr\n\t" + "nop\n\t"); + + +#ifdef CONFIG_DEBUG_BUGVERBOSE +void +handle_BUG(struct pt_regs *regs) +{ +} +#endif +#endif diff --git a/arch/cris/arch-v32/kernel/vcs_hook.c b/arch/cris/arch-v32/kernel/vcs_hook.c deleted file mode 100644 index 64d71c54c22..00000000000 --- a/arch/cris/arch-v32/kernel/vcs_hook.c +++ /dev/null @@ -1,96 +0,0 @@ -// $Id: vcs_hook.c,v 1.2 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook. This is the part running in the -// simulated program. -// - -#include "vcs_hook.h" -#include <stdarg.h> -#include <asm/arch-v32/hwregs/reg_map.h> -#include <asm/arch-v32/hwregs/intr_vect_defs.h> - -#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ -#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ - -#define HOOK_DATA(offset) ((unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define VHOOK_DATA(offset) ((volatile unsigned*) HOOK_MEM_BASE_ADDR)[offset] -#define HOOK_TRIG(funcid) do { *((unsigned *) HOOK_TRIG_ADDR) = funcid; } while(0) -#define HOOK_DATA_BYTE(offset) ((unsigned char*) HOOK_MEM_BASE_ADDR)[offset] - - -// ------------------------------------------------------------------ hook_call -int hook_call( unsigned id, unsigned pcnt, ...) { - va_list ap; - unsigned i; - unsigned ret; -#ifdef USING_SOS - PREEMPT_OFF_SAVE(); -#endif - - // pass parameters - HOOK_DATA(0) = id; - - /* Have to make hook_print_str a special case since we call with a - parameter of byte type. Should perhaps be a separate - hook_call. */ - - if (id == hook_print_str) { - int i; - char *str; - - HOOK_DATA(1) = pcnt; - - va_start(ap, pcnt); - str = (char*)va_arg(ap,unsigned); - - for (i=0; i!=pcnt; i++) { - HOOK_DATA_BYTE(8+i) = str[i]; - } - HOOK_DATA_BYTE(8+i) = 0; /* null byte */ - } - else { - va_start(ap, pcnt); - for( i = 1; i <= pcnt; i++ ) HOOK_DATA(i) = va_arg(ap,unsigned); - va_end(ap); - } - - // read from mem to make sure data has propagated to memory before trigging - *((volatile unsigned*) HOOK_MEM_BASE_ADDR); - - // trigger hook - HOOK_TRIG(id); - - // wait for call to finish - while( VHOOK_DATA(0) > 0 ) {} - - // extract return value - - ret = VHOOK_DATA(1); - -#ifdef USING_SOS - PREEMPT_RESTORE(); -#endif - return ret; -} - -unsigned -hook_buf(unsigned i) -{ - return (HOOK_DATA(i)); -} - -void print_str( const char *str ) { - int i; - for (i=1; str[i]; i++); /* find null at end of string */ - hook_call(hook_print_str, i, str); -} - -// --------------------------------------------------------------- CPU_KICK_DOG -void CPU_KICK_DOG(void) { - (void) hook_call( hook_kick_dog, 0 ); -} - -// ------------------------------------------------------- CPU_WATCHDOG_TIMEOUT -void CPU_WATCHDOG_TIMEOUT( unsigned t ) { - (void) hook_call( hook_dog_timeout, 1, t ); -} diff --git a/arch/cris/arch-v32/lib/Makefile b/arch/cris/arch-v32/lib/Makefile index 05b3ec6978d..eb4aad1f115 100644 --- a/arch/cris/arch-v32/lib/Makefile +++ b/arch/cris/arch-v32/lib/Makefile @@ -2,5 +2,6 @@ # Makefile for Etrax-specific library files.. # -lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o +lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o \ + csumcpfruser.o spinlock.o delay.o diff --git a/arch/cris/arch-v32/lib/checksum.S b/arch/cris/arch-v32/lib/checksum.S index 32e66181b82..87f3fd71ab1 100644 --- a/arch/cris/arch-v32/lib/checksum.S +++ b/arch/cris/arch-v32/lib/checksum.S @@ -1,6 +1,6 @@ /* * A fast checksum routine using movem - * Copyright (c) 1998-2001, 2003 Axis Communications AB + * Copyright (c) 1998-2007 Axis Communications AB * * csum_partial(const unsigned char * buff, int len, unsigned int sum) */ @@ -12,30 +12,23 @@ csum_partial: ;; r11 - length ;; r12 - checksum - ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the - ;; actual length when length < 40 - - cmpu.w 80,$r11 - blo _word_loop - nop - - ;; need to save the registers we use below in the movem loop - ;; this overhead is why we have a check above for breakeven length - ;; only r0 - r8 have to be saved, the other ones are clobber-able - ;; according to the ABI + ;; Optimized for large packets + subq 10*4, $r11 + blt _word_loop + move.d $r11, $acr subq 9*4,$sp - subq 10*4,$r11 ; update length for the first loop + clearf c movem $r8,[$sp] ;; do a movem checksum _mloop: movem [$r10+],$r9 ; read 10 longwords - + ;; Loop count without touching the c flag. + addoq -10*4, $acr, $acr ;; perform dword checksumming on the 10 longwords - add.d $r0,$r12 + addc $r0,$r12 addc $r1,$r12 addc $r2,$r12 addc $r3,$r12 @@ -46,60 +39,41 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords addc $r8,$r12 addc $r9,$r12 - ;; fold the carry into the checksum, to avoid having to loop the carry - ;; back into the top - - addc 0,$r12 - addc 0,$r12 ; do it again, since we might have generated a carry - - subq 10*4,$r11 - bge _mloop - nop - - addq 10*4,$r11 ; compensate for last loop underflowing length + ;; test $acr without trashing carry. + move.d $acr, $acr + bpl _mloop + ;; r11 <= acr is not really needed in the mloop, just using the dslot + ;; to prepare for what is needed after mloop. + move.d $acr, $r11 + ;; fold the last carry into r13 + addc 0, $r12 movem [$sp+],$r8 ; restore regs _word_loop: - ;; only fold if there is anything to fold. - - cmpq 0,$r12 - beq _no_fold - - ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below. - ;; r9 and r13 can be used as temporaries. + addq 10*4,$r11 ; compensate for last loop underflowing length moveq -1,$r9 ; put 0xffff in r9, faster than move.d 0xffff,r9 lsrq 16,$r9 move.d $r12,$r13 lsrq 16,$r13 ; r13 = checksum >> 16 - and.d $r9,$r12 ; checksum = checksum & 0xffff - add.d $r13,$r12 ; checksum += r13 - move.d $r12,$r13 ; do the same again, maybe we got a carry last add - lsrq 16,$r13 - and.d $r9,$r12 - add.d $r13,$r12 + and.d $r9,$r12 ; checksum = checksum & 0xffff _no_fold: - cmpq 2,$r11 + subq 2,$r11 blt _no_words - nop + add.d $r13,$r12 ; checksum += r13 ;; checksum the rest of the words - - subq 2,$r11 - _wloop: subq 2,$r11 bge _wloop addu.w [$r10+],$r12 - addq 2,$r11 - _no_words: + addq 2,$r11 ;; see if we have one odd byte more - cmpq 1,$r11 - beq _do_byte + bne _do_byte nop ret move.d $r12,$r10 diff --git a/arch/cris/arch-v32/lib/checksumcopy.S b/arch/cris/arch-v32/lib/checksumcopy.S index 9303ccbadc6..21aabe91489 100644 --- a/arch/cris/arch-v32/lib/checksumcopy.S +++ b/arch/cris/arch-v32/lib/checksumcopy.S @@ -1,6 +1,6 @@ /* * A fast checksum+copy routine using movem - * Copyright (c) 1998, 2001, 2003 Axis Communications AB + * Copyright (c) 1998-2007 Axis Communications AB * * Authors: Bjorn Wesen * @@ -16,32 +16,23 @@ csum_partial_copy_nocheck: ;; r12 - length ;; r13 - checksum - ;; check for breakeven length between movem and normal word looping versions - ;; we also do _NOT_ want to compute a checksum over more than the - ;; actual length when length < 40 - - cmpu.w 80,$r12 - blo _word_loop - nop - - ;; need to save the registers we use below in the movem loop - ;; this overhead is why we have a check above for breakeven length - ;; only r0 - r8 have to be saved, the other ones are clobber-able - ;; according to the ABI + ;; Optimized for large packets + subq 10*4, $r12 + blt _word_loop + move.d $r12, $acr subq 9*4,$sp - subq 10*4,$r12 ; update length for the first loop + clearf c movem $r8,[$sp] ;; do a movem copy and checksum - 1: ;; A failing userspace access (the read) will have this as PC. _mloop: movem [$r10+],$r9 ; read 10 longwords + addoq -10*4, $acr, $acr ; loop counter in latency cycle movem $r9,[$r11+] ; write 10 longwords ;; perform dword checksumming on the 10 longwords - - add.d $r0,$r13 + addc $r0,$r13 addc $r1,$r13 addc $r2,$r13 addc $r3,$r13 @@ -52,47 +43,30 @@ _mloop: movem [$r10+],$r9 ; read 10 longwords addc $r8,$r13 addc $r9,$r13 - ;; fold the carry into the checksum, to avoid having to loop the carry - ;; back into the top - - addc 0,$r13 - addc 0,$r13 ; do it again, since we might have generated a carry - - subq 10*4,$r12 - bge _mloop - nop - - addq 10*4,$r12 ; compensate for last loop underflowing length + ;; test $acr, without trashing carry. + move.d $acr, $acr + bpl _mloop + ;; r12 <= acr is needed after mloop and in the exception handlers. + move.d $acr, $r12 + ;; fold the last carry into r13 + addc 0, $r13 movem [$sp+],$r8 ; restore regs _word_loop: - ;; only fold if there is anything to fold. - - cmpq 0,$r13 - beq _no_fold + addq 10*4,$r12 ; compensate for last loop underflowing length ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below ;; r9 can be used as temporary. - move.d $r13,$r9 lsrq 16,$r9 ; r0 = checksum >> 16 and.d 0xffff,$r13 ; checksum = checksum & 0xffff - add.d $r9,$r13 ; checksum += r0 - move.d $r13,$r9 ; do the same again, maybe we got a carry last add - lsrq 16,$r9 - and.d 0xffff,$r13 - add.d $r9,$r13 -_no_fold: - cmpq 2,$r12 + subq 2, $r12 blt _no_words - nop + add.d $r9,$r13 ; checksum += r0 ;; copy and checksum the rest of the words - - subq 2,$r12 - 2: ;; A failing userspace access for the read below will have this as PC. _wloop: move.w [$r10+],$r9 addu.w $r9,$r13 @@ -100,12 +74,9 @@ _wloop: move.w [$r10+],$r9 bge _wloop move.w $r9,[$r11+] - addq 2,$r12 - _no_words: - ;; see if we have one odd byte more - cmpq 1,$r12 - beq _do_byte + addq 2,$r12 + bne _do_byte nop ret move.d $r13,$r10 diff --git a/arch/cris/arch-v32/lib/delay.c b/arch/cris/arch-v32/lib/delay.c new file mode 100644 index 00000000000..39f1ac9995b --- /dev/null +++ b/arch/cris/arch-v32/lib/delay.c @@ -0,0 +1,28 @@ +/* + * Precise Delay Loops for ETRAX FS + * + * Copyright (C) 2006 Axis Communications AB. + * + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/timer_defs.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/module.h> + +/* + * On ETRAX FS, we can check the free-running read-only 100MHz timer + * getting 32-bit 10ns precision, theoretically good for 42.94967295 + * seconds. Unsigned arithmetic and careful expression handles + * wrapping. + */ + +void cris_delay10ns(u32 n10ns) +{ + u32 t0 = REG_RD(timer, regi_timer0, r_time); + while (REG_RD(timer, regi_timer0, r_time) - t0 < n10ns) + ; +} +EXPORT_SYMBOL(cris_delay10ns); diff --git a/arch/cris/arch-v32/lib/spinlock.S b/arch/cris/arch-v32/lib/spinlock.S index 2437ae7f6ed..79087ef59a1 100644 --- a/arch/cris/arch-v32/lib/spinlock.S +++ b/arch/cris/arch-v32/lib/spinlock.S @@ -12,11 +12,11 @@ cris_spin_lock: clearf p -1: test.d [$r10] +1: test.b [$r10] beq 1b clearf p ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret @@ -24,10 +24,10 @@ cris_spin_lock: cris_spin_trylock: clearf p -1: move.d [$r10], $r11 +1: move.b [$r10], $r11 ax - clear.d [$r10] + clear.b [$r10] bcs 1b clearf p ret - move.d $r11,$r10 + movu.b $r11,$r10 diff --git a/arch/cris/arch-v32/mach-a3/Kconfig b/arch/cris/arch-v32/mach-a3/Kconfig new file mode 100644 index 00000000000..a4df06d5997 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/Kconfig @@ -0,0 +1,110 @@ +if CRIS_MACH_ARTPEC3 + +menu "Artpec-3 options" + depends on CRIS_MACH_ARTPEC3 + +config ETRAX_DRAM_VIRTUAL_BASE + hex + default "c0000000" + +config ETRAX_L2CACHE + bool + default y + +config ETRAX_SERIAL_PORTS + int + default 5 + +config ETRAX_DDR + bool + default y + +config ETRAX_DDR2_MRS + hex "DDR2 MRS" + default "0" + +config ETRAX_DDR2_TIMING + hex "DDR2 SDRAM timing" + default "0" + help + SDRAM timing parameters. + +config ETRAX_DDR2_CONFIG + hex "DDR2 config" + default "0" + +config ETRAX_PIO_CE0_CFG + hex "PIO CE0 configuration" + default "0" + +config ETRAX_PIO_CE1_CFG + hex "PIO CE1 configuration" + default "0" + +config ETRAX_PIO_CE2_CFG + hex "PIO CE2 configuration" + default "0" + +config ETRAX_DEF_GIO_PA_OE + hex "GIO_PA_OE" + default "00000000" + help + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PA_OUT + hex "GIO_PA_OUT" + default "00000000" + help + Configures the initial data for the general port A bits. Most + products should use 00 here. + +config ETRAX_DEF_GIO_PB_OE + hex "GIO_PB_OE" + default "000000000" + help + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PB_OUT + hex "GIO_PB_OUT" + default "000000000" + help + Configures the initial data for the general port B bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PC_OE + hex "GIO_PC_OE" + default "00000" + help + Configures the direction of general port C bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PC_OUT + hex "GIO_PC_OUT" + default "00000" + help + Configures the initial data for the general port C bits. Most + products should use 00000 here. + +endmenu + +endif diff --git a/arch/cris/arch-v32/mach-a3/Makefile b/arch/cris/arch-v32/mach-a3/Makefile new file mode 100644 index 00000000000..41fa6a6893a --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile,v 1.3 2007/03/13 11:57:46 starvik Exp $ +# +# Makefile for the linux kernel. +# + +obj-y := dma.o pinmux.o io.o arbiter.o +obj-$(CONFIG_ETRAX_VCS_SIM) += vcs_hook.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o + +clean: + diff --git a/arch/cris/arch-v32/mach-a3/arbiter.c b/arch/cris/arch-v32/mach-a3/arbiter.c new file mode 100644 index 00000000000..8b924db71c9 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/arbiter.c @@ -0,0 +1,634 @@ +/* + * Memory arbiter functions. Allocates bandwidth through the + * arbiter and sets up arbiter breakpoints. + * + * The algorithm first assigns slots to the clients that has specified + * bandwidth (e.g. ethernet) and then the remaining slots are divided + * on all the active clients. + * + * Copyright (c) 2004-2007 Axis Communications AB. + * + * The artpec-3 has two arbiters. The memory hierarchy looks like this: + * + * + * CPU DMAs + * | | + * | | + * -------------- ------------------ + * | foo arbiter|----| Internal memory| + * -------------- ------------------ + * | + * -------------- + * | L2 cache | + * -------------- + * | + * h264 etc | + * | | + * | | + * -------------- + * | bar arbiter| + * -------------- + * | + * --------- + * | SDRAM | + * --------- + * + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_foo_defs.h> +#include <hwregs/marb_bar_defs.h> +#include <arbiter.h> +#include <hwregs/intr_vect.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/irq_regs.h> + +#define D(x) + +struct crisv32_watch_entry { + unsigned long instance; + watch_callback *cb; + unsigned long start; + unsigned long end; + int used; +}; + +#define NUMBER_OF_BP 4 +#define SDRAM_BANDWIDTH 400000000 +#define INTMEM_BANDWIDTH 400000000 +#define NBR_OF_SLOTS 64 +#define NBR_OF_REGIONS 2 +#define NBR_OF_CLIENTS 15 +#define ARBITERS 2 +#define UNASSIGNED 100 + +struct arbiter { + unsigned long instance; + int nbr_regions; + int nbr_clients; + int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; + int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +}; + +static struct crisv32_watch_entry watches[ARBITERS][NUMBER_OF_BP] = +{ + { + {regi_marb_foo_bp0}, + {regi_marb_foo_bp1}, + {regi_marb_foo_bp2}, + {regi_marb_foo_bp3} + }, + { + {regi_marb_bar_bp0}, + {regi_marb_bar_bp1}, + {regi_marb_bar_bp2}, + {regi_marb_bar_bp3} + } +}; + +struct arbiter arbiters[ARBITERS] = +{ + { /* L2 cache arbiter */ + .instance = regi_marb_foo, + .nbr_regions = 2, + .nbr_clients = 15 + }, + { /* DDR2 arbiter */ + .instance = regi_marb_bar, + .nbr_regions = 1, + .nbr_clients = 9 + } +}; + +static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; + +DEFINE_SPINLOCK(arbiter_lock); + +static irqreturn_t +crisv32_foo_arbiter_irq(int irq, void *dev_id); +static irqreturn_t +crisv32_bar_arbiter_irq(int irq, void *dev_id); + +/* + * "I'm the arbiter, I know the score. + * From square one I'll be watching all 64." + * (memory arbiter slots, that is) + * + * Or in other words: + * Program the memory arbiter slots for "region" according to what's + * in requested_slots[] and active_clients[], while minimizing + * latency. A caller may pass a non-zero positive amount for + * "unused_slots", which must then be the unallocated, remaining + * number of slots, free to hand out to any client. + */ + +static void crisv32_arbiter_config(int arbiter, int region, int unused_slots) +{ + int slot; + int client; + int interval = 0; + + /* + * This vector corresponds to the hardware arbiter slots (see + * the hardware documentation for semantics). We initialize + * each slot with a suitable sentinel value outside the valid + * range {0 .. NBR_OF_CLIENTS - 1} and replace them with + * client indexes. Then it's fed to the hardware. + */ + s8 val[NBR_OF_SLOTS]; + + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + val[slot] = -1; + + for (client = 0; client < arbiters[arbiter].nbr_clients; client++) { + int pos; + /* Allocate the requested non-zero number of slots, but + * also give clients with zero-requests one slot each + * while stocks last. We do the latter here, in client + * order. This makes sure zero-request clients are the + * first to get to any spare slots, else those slots + * could, when bandwidth is allocated close to the limit, + * all be allocated to low-index non-zero-request clients + * in the default-fill loop below. Another positive but + * secondary effect is a somewhat better spread of the + * zero-bandwidth clients in the vector, avoiding some of + * the latency that could otherwise be caused by the + * partitioning of non-zero-bandwidth clients at low + * indexes and zero-bandwidth clients at high + * indexes. (Note that this spreading can only affect the + * unallocated bandwidth.) All the above only matters for + * memory-intensive situations, of course. + */ + if (!arbiters[arbiter].requested_slots[region][client]) { + /* + * Skip inactive clients. Also skip zero-slot + * allocations in this pass when there are no known + * free slots. + */ + if (!arbiters[arbiter].active_clients[region][client] || + unused_slots <= 0) + continue; + + unused_slots--; + + /* Only allocate one slot for this client. */ + interval = NBR_OF_SLOTS; + } else + interval = NBR_OF_SLOTS / + arbiters[arbiter].requested_slots[region][client]; + + pos = 0; + while (pos < NBR_OF_SLOTS) { + if (val[pos] >= 0) + pos++; + else { + val[pos] = client; + pos += interval; + } + } + } + + client = 0; + for (slot = 0; slot < NBR_OF_SLOTS; slot++) { + /* + * Allocate remaining slots in round-robin + * client-number order for active clients. For this + * pass, we ignore requested bandwidth and previous + * allocations. + */ + if (val[slot] < 0) { + int first = client; + while (!arbiters[arbiter].active_clients[region][client]) { + client = (client + 1) % + arbiters[arbiter].nbr_clients; + if (client == first) + break; + } + val[slot] = client; + client = (client + 1) % arbiters[arbiter].nbr_clients; + } + if (arbiter == 0) { + if (region == EXT_REGION) + REG_WR_INT_VECT(marb_foo, regi_marb_foo, + rw_l2_slots, slot, val[slot]); + else if (region == INT_REGION) + REG_WR_INT_VECT(marb_foo, regi_marb_foo, + rw_intm_slots, slot, val[slot]); + } else { + REG_WR_INT_VECT(marb_bar, regi_marb_bar, + rw_ddr2_slots, slot, val[slot]); + } + } +} + +extern char _stext, _etext; + +static void crisv32_arbiter_init(void) +{ + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * CPU caches are always set to active, but with zero + * bandwidth allocated. It should be ok to allocate zero + * bandwidth for the caches, because DMA for other channels + * will supposedly finish, once their programmed amount is + * done, and then the caches will get access according to the + * "fixed scheme" for unclaimed slots. Though, if for some + * use-case somewhere, there's a maximum CPU latency for + * e.g. some interrupt, we have to start allocating specific + * bandwidth for the CPU caches too. + */ + arbiters[0].active_clients[EXT_REGION][11] = 1; + arbiters[0].active_clients[EXT_REGION][12] = 1; + crisv32_arbiter_config(0, EXT_REGION, 0); + crisv32_arbiter_config(0, INT_REGION, 0); + crisv32_arbiter_config(1, EXT_REGION, 0); + + if (request_irq(MEMARB_FOO_INTR_VECT, crisv32_foo_arbiter_irq, + IRQF_DISABLED, "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + + if (request_irq(MEMARB_BAR_INTR_VECT, crisv32_bar_arbiter_irq, + IRQF_DISABLED, "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + +#ifndef CONFIG_ETRAX_KGDB + /* Global watch for writes to kernel text segment. */ + crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, + MARB_CLIENTS(arbiter_all_clients, arbiter_bar_all_clients), + arbiter_all_write, NULL); +#endif + + /* Set up max burst sizes by default */ + REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_h264_wr_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_ccd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_wr_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vin_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_rd_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_vout_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_sclr_fifo_burst, 3); + REG_WR_INT(marb_bar, regi_marb_bar, rw_l2cache_burst, 3); +} + +int crisv32_arbiter_allocate_bandwidth(int client, int region, + unsigned long bandwidth) +{ + int i; + int total_assigned = 0; + int total_clients = 0; + int req; + int arbiter = 0; + + crisv32_arbiter_init(); + + if (client & 0xffff0000) { + arbiter = 1; + client >>= 16; + } + + for (i = 0; i < arbiters[arbiter].nbr_clients; i++) { + total_assigned += arbiters[arbiter].requested_slots[region][i]; + total_clients += arbiters[arbiter].active_clients[region][i]; + } + + /* Avoid division by 0 for 0-bandwidth requests. */ + req = bandwidth == 0 + ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + /* + * We make sure that there are enough slots only for non-zero + * requests. Requesting 0 bandwidth *may* allocate slots, + * though if all bandwidth is allocated, such a client won't + * get any and will have to rely on getting memory access + * according to the fixed scheme that's the default when one + * of the slot-allocated clients doesn't claim their slot. + */ + if (total_assigned + req > NBR_OF_SLOTS) + return -ENOMEM; + + arbiters[arbiter].active_clients[region][client] = 1; + arbiters[arbiter].requested_slots[region][client] = req; + crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned); + + /* Propagate allocation from foo to bar */ + if (arbiter == 0) + crisv32_arbiter_allocate_bandwidth(8 << 16, + EXT_REGION, bandwidth); + return 0; +} + +/* + * Main entry for bandwidth deallocation. + * + * Strictly speaking, for a somewhat constant set of clients where + * each client gets a constant bandwidth and is just enabled or + * disabled (somewhat dynamically), no action is necessary here to + * avoid starvation for non-zero-allocation clients, as the allocated + * slots will just be unused. However, handing out those unused slots + * to active clients avoids needless latency if the "fixed scheme" + * would give unclaimed slots to an eager low-index client. + */ + +void crisv32_arbiter_deallocate_bandwidth(int client, int region) +{ + int i; + int total_assigned = 0; + int arbiter = 0; + + if (client & 0xffff0000) + arbiter = 1; + + arbiters[arbiter].requested_slots[region][client] = 0; + arbiters[arbiter].active_clients[region][client] = 0; + + for (i = 0; i < arbiters[arbiter].nbr_clients; i++) + total_assigned += arbiters[arbiter].requested_slots[region][i]; + + crisv32_arbiter_config(arbiter, region, NBR_OF_SLOTS - total_assigned); +} + +int crisv32_arbiter_watch(unsigned long start, unsigned long size, + unsigned long clients, unsigned long accesses, + watch_callback *cb) +{ + int i; + int arbiter; + int used[2]; + int ret = 0; + + crisv32_arbiter_init(); + + if (start > 0x80000000) { + printk(KERN_ERR "Arbiter: %lX doesn't look like a " + "physical address", start); + return -EFAULT; + } + + spin_lock(&arbiter_lock); + + if (clients & 0xffff) + used[0] = 1; + if (clients & 0xffff0000) + used[1] = 1; + + for (arbiter = 0; arbiter < ARBITERS; arbiter++) { + if (!used[arbiter]) + continue; + + for (i = 0; i < NUMBER_OF_BP; i++) { + if (!watches[arbiter][i].used) { + unsigned intr_mask; + if (arbiter) + intr_mask = REG_RD_INT(marb_bar, + regi_marb_bar, rw_intr_mask); + else + intr_mask = REG_RD_INT(marb_foo, + regi_marb_foo, rw_intr_mask); + + watches[arbiter][i].used = 1; + watches[arbiter][i].start = start; + watches[arbiter][i].end = start + size; + watches[arbiter][i].cb = cb; + + ret |= (i + 1) << (arbiter + 8); + if (arbiter) { + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_first_addr, + watches[arbiter][i].start); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_last_addr, + watches[arbiter][i].end); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_op, accesses); + REG_WR_INT(marb_bar_bp, + watches[arbiter][i].instance, + rw_clients, + clients & 0xffff); + } else { + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_first_addr, + watches[arbiter][i].start); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_last_addr, + watches[arbiter][i].end); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_op, accesses); + REG_WR_INT(marb_foo_bp, + watches[arbiter][i].instance, + rw_clients, clients >> 16); + } + + if (i == 0) + intr_mask |= 1; + else if (i == 1) + intr_mask |= 2; + else if (i == 2) + intr_mask |= 4; + else if (i == 3) + intr_mask |= 8; + + if (arbiter) + REG_WR_INT(marb_bar, regi_marb_bar, + rw_intr_mask, intr_mask); + else + REG_WR_INT(marb_foo, regi_marb_foo, + rw_intr_mask, intr_mask); + + spin_unlock(&arbiter_lock); + + break; + } + } + } + spin_unlock(&arbiter_lock); + if (ret) + return ret; + else + return -ENOMEM; +} + +int crisv32_arbiter_unwatch(int id) +{ + int arbiter; + int intr_mask; + + crisv32_arbiter_init(); + + spin_lock(&arbiter_lock); + + for (arbiter = 0; arbiter < ARBITERS; arbiter++) { + int id2; + + if (arbiter) + intr_mask = REG_RD_INT(marb_bar, regi_marb_bar, + rw_intr_mask); + else + intr_mask = REG_RD_INT(marb_foo, regi_marb_foo, + rw_intr_mask); + + id2 = (id & (0xff << (arbiter + 8))) >> (arbiter + 8); + if (id2 == 0) + continue; + id2--; + if ((id2 >= NUMBER_OF_BP) || (!watches[arbiter][id2].used)) { + spin_unlock(&arbiter_lock); + return -EINVAL; + } + + memset(&watches[arbiter][id2], 0, + sizeof(struct crisv32_watch_entry)); + + if (id2 == 0) + intr_mask &= ~1; + else if (id2 == 1) + intr_mask &= ~2; + else if (id2 == 2) + intr_mask &= ~4; + else if (id2 == 3) + intr_mask &= ~8; + + if (arbiter) + REG_WR_INT(marb_bar, regi_marb_bar, rw_intr_mask, + intr_mask); + else + REG_WR_INT(marb_foo, regi_marb_foo, rw_intr_mask, + intr_mask); + } + + spin_unlock(&arbiter_lock); + return 0; +} + +extern void show_registers(struct pt_regs *regs); + + +static irqreturn_t +crisv32_foo_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_foo_r_masked_intr masked_intr = + REG_RD(marb_foo, regi_marb_foo, r_masked_intr); + reg_marb_foo_bp_r_brk_clients r_clients; + reg_marb_foo_bp_r_brk_addr r_addr; + reg_marb_foo_bp_r_brk_op r_op; + reg_marb_foo_bp_r_brk_first_client r_first; + reg_marb_foo_bp_r_brk_size r_size; + reg_marb_foo_bp_rw_ack ack = {0}; + reg_marb_foo_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + unsigned arbiter = (unsigned)dev_id; + + masked_intr = REG_RD(marb_foo, regi_marb_foo, r_masked_intr); + + if (masked_intr.bp0) + watch = &watches[arbiter][0]; + else if (masked_intr.bp1) + watch = &watches[arbiter][1]; + else if (masked_intr.bp2) + watch = &watches[arbiter][2]; + else if (masked_intr.bp3) + watch = &watches[arbiter][3]; + else + return IRQ_NONE; + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_foo_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_foo_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_foo_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_foo_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_foo_bp, watch->instance, r_brk_size); + + printk(KERN_DEBUG "Arbiter IRQ\n"); + printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_foo_bp_r_brk_size, r_size)); + + REG_WR(marb_foo_bp, watch->instance, rw_ack, ack); + REG_WR(marb_foo, regi_marb_foo, rw_ack_intr, ack_intr); + + printk(KERN_DEBUG "IRQ occured at %X\n", (unsigned)get_irq_regs()); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} + +static irqreturn_t +crisv32_bar_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_bar_r_masked_intr masked_intr = + REG_RD(marb_bar, regi_marb_bar, r_masked_intr); + reg_marb_bar_bp_r_brk_clients r_clients; + reg_marb_bar_bp_r_brk_addr r_addr; + reg_marb_bar_bp_r_brk_op r_op; + reg_marb_bar_bp_r_brk_first_client r_first; + reg_marb_bar_bp_r_brk_size r_size; + reg_marb_bar_bp_rw_ack ack = {0}; + reg_marb_bar_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + unsigned arbiter = (unsigned)dev_id; + + masked_intr = REG_RD(marb_bar, regi_marb_bar, r_masked_intr); + + if (masked_intr.bp0) + watch = &watches[arbiter][0]; + else if (masked_intr.bp1) + watch = &watches[arbiter][1]; + else if (masked_intr.bp2) + watch = &watches[arbiter][2]; + else if (masked_intr.bp3) + watch = &watches[arbiter][3]; + else + return IRQ_NONE; + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_bar_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_bar_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_bar_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_bar_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_bar_bp, watch->instance, r_brk_size); + + printk(KERN_DEBUG "Arbiter IRQ\n"); + printk(KERN_DEBUG "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_bar_bp_r_brk_size, r_size)); + + REG_WR(marb_bar_bp, watch->instance, rw_ack, ack); + REG_WR(marb_bar, regi_marb_bar, rw_ack_intr, ack_intr); + + printk(KERN_DEBUG "IRQ occured at %X\n", (unsigned)get_irq_regs()->erp); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} + diff --git a/arch/cris/arch-v32/mach-a3/cpufreq.c b/arch/cris/arch-v32/mach-a3/cpufreq.c new file mode 100644 index 00000000000..8e5a3cab8ad --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/cpufreq.c @@ -0,0 +1,153 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/clkgen_defs.h> +#include <hwregs/ddr2_defs.h> + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_sdram_freq_notifier_block = { + .notifier_call = cris_sdram_freq_notifier +}; + +static struct cpufreq_frequency_table cris_freq_table[] = { + {0x01, 6000}, + {0x02, 200000}, + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) +{ + reg_clkgen_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + return clk_ctrl.pll ? 200000 : 6000; +} + +static void cris_freq_set_cpu_state(unsigned int state) +{ + int i = 0; + struct cpufreq_freqs freqs; + reg_clkgen_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + +#ifdef CONFIG_SMP + for_each_present_cpu(i) +#endif + { + freqs.old = cris_freq_get_cpu_frequency(i); + freqs.new = cris_freq_table[state].frequency; + freqs.cpu = i; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_disable(); + + /* Even though we may be SMP they will share the same clock + * so all settings are made on CPU0. */ + if (cris_freq_table[state].frequency == 200000) + clk_ctrl.pll = 1; + else + clk_ctrl.pll = 0; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int cris_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]); +} + +static int cris_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target(policy, cris_freq_table, + target_freq, relation, &newstate)) + return -EINVAL; + + cris_freq_set_cpu_state(newstate); + + return 0; +} + +static int cris_freq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->cur = cris_freq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu); + + return 0; +} + + +static int cris_freq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + + +static struct freq_attr *cris_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver cris_freq_driver = { + .get = cris_freq_get_cpu_frequency, + .verify = cris_freq_verify, + .target = cris_freq_target, + .init = cris_freq_cpu_init, + .exit = cris_freq_cpu_exit, + .name = "cris_freq", + .owner = THIS_MODULE, + .attr = cris_freq_attr, +}; + +static int __init cris_freq_init(void) +{ + int ret; + ret = cpufreq_register_driver(&cris_freq_driver); + cpufreq_register_notifier(&cris_sdram_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + int i; + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_PRECHANGE) { + reg_ddr2_rw_cfg cfg = + REG_RD(ddr2, regi_ddr2_ctrl, rw_cfg); + cfg.ref_interval = (freqs->new == 200000 ? 1560 : 46); + + if (freqs->new == 200000) + for (i = 0; i < 50000; i++); + REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing); + } + return 0; +} + + +module_init(cris_freq_init); diff --git a/arch/cris/arch-v32/mach-a3/dma.c b/arch/cris/arch-v32/mach-a3/dma.c new file mode 100644 index 00000000000..25f236ef0b8 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/dma.c @@ -0,0 +1,185 @@ +/* Wrapper for DMA channel allocator that starts clocks etc */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <asm/arch/mach/dma.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <hwregs/clkgen_defs.h> +#include <hwregs/strmux_defs.h> +#include <linux/errno.h> +#include <asm/system.h> +#include <arbiter.h> + +static char used_dma_channels[MAX_DMA_CHANNELS]; +static const char *used_dma_channels_users[MAX_DMA_CHANNELS]; + +static DEFINE_SPINLOCK(dma_lock); + +int crisv32_request_dma(unsigned int dmanr, const char *device_id, + unsigned options, unsigned int bandwidth, enum dma_owner owner) +{ + unsigned long flags; + reg_clkgen_rw_clk_ctrl clk_ctrl; + reg_strmux_rw_cfg strmux_cfg; + + if (crisv32_arbiter_allocate_bandwidth(dmanr, + options & DMA_INT_MEM ? INT_REGION : EXT_REGION, + bandwidth)) + return -ENOMEM; + + spin_lock_irqsave(&dma_lock, flags); + + if (used_dma_channels[dmanr]) { + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) + printk(KERN_ERR "Failed to request DMA %i for %s, " + "already allocated by %s\n", + dmanr, + device_id, + used_dma_channels_users[dmanr]); + + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + spin_unlock_irqrestore(&dma_lock, flags); + return -EBUSY; + } + clk_ctrl = REG_RD(clkgen, regi_clkgen, rw_clk_ctrl); + strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg); + + switch (dmanr) { + case 0: + case 1: + clk_ctrl.dma0_1_eth = 1; + break; + case 2: + case 3: + clk_ctrl.dma2_3_strcop = 1; + break; + case 4: + case 5: + clk_ctrl.dma4_5_iop = 1; + break; + case 6: + case 7: + clk_ctrl.sser_ser_dma6_7 = 1; + break; + case 9: + case 11: + clk_ctrl.dma9_11 = 1; + break; +#if MAX_DMA_CHANNELS-1 != 11 +#error Check dma.c +#endif + default: + spin_unlock_irqrestore(&dma_lock, flags); + if (options & DMA_VERBOSE_ON_ERROR) + printk(KERN_ERR "Failed to request DMA %i for %s, " + "only 0-%i valid)\n", + dmanr, device_id, MAX_DMA_CHANNELS-1); + + if (options & DMA_PANIC_ON_ERROR) + panic("request_dma error!"); + return -EINVAL; + } + + switch (owner) { + case dma_eth: + if (dmanr == 0) + strmux_cfg.dma0 = regk_strmux_eth; + else if (dmanr == 1) + strmux_cfg.dma1 = regk_strmux_eth; + else + panic("Invalid DMA channel for eth\n"); + break; + case dma_ser0: + if (dmanr == 0) + strmux_cfg.dma0 = regk_strmux_ser0; + else if (dmanr == 1) + strmux_cfg.dma1 = regk_strmux_ser0; + else + panic("Invalid DMA channel for ser0\n"); + break; + case dma_ser3: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_ser3; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_ser3; + else + panic("Invalid DMA channel for ser3\n"); + break; + case dma_strp: + if (dmanr == 2) + strmux_cfg.dma2 = regk_strmux_strcop; + else if (dmanr == 3) + strmux_cfg.dma3 = regk_strmux_strcop; + else + panic("Invalid DMA channel for strp\n"); + break; + case dma_ser1: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_ser1; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_ser1; + else + panic("Invalid DMA channel for ser1\n"); + break; + case dma_iop: + if (dmanr == 4) + strmux_cfg.dma4 = regk_strmux_iop; + else if (dmanr == 5) + strmux_cfg.dma5 = regk_strmux_iop; + else + panic("Invalid DMA channel for iop\n"); + break; + case dma_ser2: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_ser2; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_ser2; + else + panic("Invalid DMA channel for ser2\n"); + break; + case dma_sser: + if (dmanr == 6) + strmux_cfg.dma6 = regk_strmux_sser; + else if (dmanr == 7) + strmux_cfg.dma7 = regk_strmux_sser; + else + panic("Invalid DMA channel for sser\n"); + break; + case dma_ser4: + if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_ser4; + else + panic("Invalid DMA channel for ser4\n"); + break; + case dma_jpeg: + if (dmanr == 9) + strmux_cfg.dma9 = regk_strmux_jpeg; + else + panic("Invalid DMA channel for JPEG\n"); + break; + case dma_h264: + if (dmanr == 11) + strmux_cfg.dma11 = regk_strmux_h264; + else + panic("Invalid DMA channel for H264\n"); + break; + } + + used_dma_channels[dmanr] = 1; + used_dma_channels_users[dmanr] = device_id; + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); + spin_unlock_irqrestore(&dma_lock, flags); + return 0; +} + +void crisv32_free_dma(unsigned int dmanr) +{ + spin_lock(&dma_lock); + used_dma_channels[dmanr] = 0; + spin_unlock(&dma_lock); +} diff --git a/arch/cris/arch-v32/mach-a3/dram_init.S b/arch/cris/arch-v32/mach-a3/dram_init.S new file mode 100644 index 00000000000..94d6b41cb29 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/dram_init.S @@ -0,0 +1,104 @@ +/* + * DDR SDRAM initialization - alter with care + * This file is intended to be included from other assembler files + * + * Note: This file may not modify r8 or r9 because they are used to + * carry information from the decompresser to the kernel + * + * Copyright (C) 2005-2007 Axis Communications AB + * + * Authors: Mikael Starvik <starvik@axis.com> + */ + +/* Just to be certain the config file is included, we include it here + * explicitely instead of depending on it being included in the file that + * uses this code. + */ + +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/ddr2_defs_asm.h> + + ;; WARNING! The registers r8 and r9 are used as parameters carrying + ;; information from the decompressor (if the kernel was compressed). + ;; They should not be used in the code below. + + ;; Refer to ddr2 MDS for initialization sequence + + ; Start clock + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_cfg), $r0 + move.d REG_STATE(ddr2, rw_phy_cfg, en, yes), $r1 + move.d $r1, [$r0] + + ; Reset phy and start calibration + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_phy_ctrl), $r0 + move.d REG_STATE(ddr2, rw_phy_ctrl, rst, yes) | \ + REG_STATE(ddr2, rw_phy_ctrl, cal_rst, yes), $r1 + move.d $r1, [$r0] + move.d REG_STATE(ddr2, rw_phy_ctrl, cal_start, yes), $r1 + move.d $r1, [$r0] + + ; 2. Wait 200us + move.d 10000, $r2 +1: bne 1b + subq 1, $r2 + + ; Issue commands + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_ctrl), $r0 + move.d sdram_commands_start, $r2 +command_loop: + movu.b [$r2+], $r1 + movu.w [$r2+], $r3 +do_cmd: + lslq 16, $r1 + or.d $r3, $r1 + move.d $r1, [$r0] + cmp.d sdram_commands_end, $r2 + blo command_loop + nop + + ; Set timing + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing), $r0 + move.d CONFIG_ETRAX_DDR2_TIMING, $r1 + move.d $r1, [$r0] + + ; Set latency + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_latency), $r0 + move.d 0x13, $r1 + move.d $r1, [$r0] + + ; Set configuration + move.d REG_ADDR(ddr2, regi_ddr2_ctrl, rw_cfg), $r0 + move.d CONFIG_ETRAX_DDR2_CONFIG, $r1 + move.d $r1, [$r0] + + ba after_sdram_commands + nop + +sdram_commands_start: + .byte regk_ddr2_deselect + .word 0 + .byte regk_ddr2_pre + .word regk_ddr2_pre_all + .byte regk_ddr2_emrs2 + .word 0 + .byte regk_ddr2_emrs3 + .word 0 + .byte regk_ddr2_emrs + .word regk_ddr2_dll_en + .byte regk_ddr2_mrs + .word regk_ddr2_dll_rst + .byte regk_ddr2_pre + .word regk_ddr2_pre_all + .byte regk_ddr2_ref + .word 0 + .byte regk_ddr2_ref + .word 0 + .byte regk_ddr2_mrs + .word CONFIG_ETRAX_DDR2_MRS & 0xffff + .byte regk_ddr2_emrs + .word regk_ddr2_ocd_default | regk_ddr2_dll_en + .byte regk_ddr2_emrs + .word regk_ddr2_ocd_exit | regk_ddr2_dll_en | (CONFIG_ETRAX_DDR2_MRS >> 16) +sdram_commands_end: + .align 1 +after_sdram_commands: diff --git a/arch/cris/arch-v32/mach-a3/hw_settings.S b/arch/cris/arch-v32/mach-a3/hw_settings.S new file mode 100644 index 00000000000..258a6329cd4 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/hw_settings.S @@ -0,0 +1,51 @@ +/* + * This table is used by some tools to extract hardware parameters. + * The table should be included in the kernel and the decompressor. + * Don't forget to update the tools if you change this table. + * + * Copyright (C) 2001-2007 Axis Communications AB + * + * Authors: Mikael Starvik <starvik@axis.com> + */ + +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/ddr2_defs_asm.h> +#include <hwregs/asm/gio_defs_asm.h> + + .ascii "HW_PARAM_MAGIC" ; Magic number + .dword 0xc0004000 ; Kernel start address + + ; Debug port +#ifdef CONFIG_ETRAX_DEBUG_PORT0 + .dword 0 +#elif defined(CONFIG_ETRAX_DEBUG_PORT1) + .dword 1 +#elif defined(CONFIG_ETRAX_DEBUG_PORT2) + .dword 2 +#elif defined(CONFIG_ETRAX_DEBUG_PORT3) + .dword 3 +#else + .dword 4 ; No debug +#endif + + ; Register values + .dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_cfg) + .dword CONFIG_ETRAX_DDR2_CONFIG + .dword REG_ADDR(ddr2, regi_ddr2_ctrl, rw_timing) + .dword CONFIG_ETRAX_DDR2_TIMING + .dword CONFIG_ETRAX_DDR2_MRS + + .dword REG_ADDR(gio, regi_gio, rw_pa_dout) + .dword CONFIG_ETRAX_DEF_GIO_PA_OUT + .dword REG_ADDR(gio, regi_gio, rw_pa_oe) + .dword CONFIG_ETRAX_DEF_GIO_PA_OE + .dword REG_ADDR(gio, regi_gio, rw_pb_dout) + .dword CONFIG_ETRAX_DEF_GIO_PB_OUT + .dword REG_ADDR(gio, regi_gio, rw_pb_oe) + .dword CONFIG_ETRAX_DEF_GIO_PB_OE + .dword REG_ADDR(gio, regi_gio, rw_pc_dout) + .dword CONFIG_ETRAX_DEF_GIO_PC_OUT + .dword REG_ADDR(gio, regi_gio, rw_pc_oe) + .dword CONFIG_ETRAX_DEF_GIO_PC_OE + + .dword 0 ; No more register values diff --git a/arch/cris/arch-v32/mach-a3/io.c b/arch/cris/arch-v32/mach-a3/io.c new file mode 100644 index 00000000000..9eeaf3eca47 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/io.c @@ -0,0 +1,149 @@ +/* + * Helper functions for I/O pins. + * + * Copyright (c) 2005-2007 Axis Communications AB. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/arch/mach/pinmux.h> +#include <hwregs/gio_defs.h> + +struct crisv32_ioport crisv32_ioports[] = { + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din), + 32 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din), + 32 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din), + 16 + }, +}; + +#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) + +struct crisv32_iopin crisv32_led_net0_green; +struct crisv32_iopin crisv32_led_net0_red; +struct crisv32_iopin crisv32_led2_green; +struct crisv32_iopin crisv32_led2_red; +struct crisv32_iopin crisv32_led3_green; +struct crisv32_iopin crisv32_led3_red; + +/* Dummy port used when green LED and red LED is on the same bit */ +static unsigned long io_dummy; +static struct crisv32_ioport dummy_port = { + &io_dummy, + &io_dummy, + &io_dummy, + 32 +}; +static struct crisv32_iopin dummy_led = { + &dummy_port, + 0 +}; + +static int __init crisv32_io_init(void) +{ + int ret = 0; + + u32 i; + + /* Locks *should* be dynamically initialized. */ + for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++) + spin_lock_init(&crisv32_ioports[i].lock); + spin_lock_init(&dummy_port.lock); + + /* Initialize LEDs */ +#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)) + ret += crisv32_io_get_name(&crisv32_led_net0_green, + CONFIG_ETRAX_LED_G_NET0); + crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) { + ret += crisv32_io_get_name(&crisv32_led_net0_red, + CONFIG_ETRAX_LED_R_NET0); + crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out); + } else + crisv32_led_net0_red = dummy_led; +#endif + + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G); + ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R); + ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G); + ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R); + + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); + + return ret; +} + +__initcall(crisv32_io_init); + +int crisv32_io_get(struct crisv32_iopin *iopin, + unsigned int port, unsigned int pin) +{ + if (port > NBR_OF_PORTS) + return -EINVAL; + if (port > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name) +{ + int port; + int pin; + + if (toupper(*name) == 'P') + name++; + + if (toupper(*name) < 'A' || toupper(*name) > 'E') + return -EINVAL; + + port = toupper(*name) - 'A'; + name++; + pin = simple_strtoul(name, NULL, 10); + + if (pin < 0 || pin > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio)) + return -EIO; + + return 0; +} + +#ifdef CONFIG_PCI +/* PCI I/O access stuff */ +struct cris_io_operations *cris_iops = NULL; +EXPORT_SYMBOL(cris_iops); +#endif + diff --git a/arch/cris/arch-v32/mach-a3/pinmux.c b/arch/cris/arch-v32/mach-a3/pinmux.c new file mode 100644 index 00000000000..0a28c9bedfb --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/pinmux.c @@ -0,0 +1,386 @@ +/* + * Allocator for I/O pins. All pins are allocated to GPIO at bootup. + * Unassigned pins and GPIO pins can be allocated to a fixed interface + * or the I/O processor instead. + * + * Copyright (c) 2005-2007 Axis Communications AB. + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <pinmux.h> +#include <hwregs/pinmux_defs.h> +#include <hwregs/clkgen_defs.h> + +#undef DEBUG + +#define PINS 80 +#define PORT_PINS 32 +#define PORTS 3 + +static char pins[PINS]; +static DEFINE_SPINLOCK(pinmux_lock); + +static void crisv32_pinmux_set(int port); + +int +crisv32_pinmux_init(void) +{ + static int initialized; + + if (!initialized) { + initialized = 1; + REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); + crisv32_pinmux_alloc(PORT_A, 0, 31, pinmux_gpio); + crisv32_pinmux_alloc(PORT_B, 0, 31, pinmux_gpio); + crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_gpio); + } + + return 0; +} + +int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port >= PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) { + if ((pins[port * PORT_PINS + i] != pinmux_none) && + (pins[port * PORT_PINS + i] != pinmux_gpio) && + (pins[port * PORT_PINS + i] != mode)) { + spin_unlock_irqrestore(&pinmux_lock, flags); +#ifdef DEBUG + panic("Pinmux alloc failed!\n"); +#endif + return -EPERM; + } + } + + for (i = first_pin; i <= last_pin; i++) + pins[port * PORT_PINS + i] = mode; + + crisv32_pinmux_set(port); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int +crisv32_pinmux_alloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + reg_clkgen_rw_clk_ctrl clk_ctrl = REG_RD(clkgen, regi_clkgen, + rw_clk_ctrl); + + switch (function) { + case pinmux_eth: + clk_ctrl.eth = regk_clkgen_yes; + clk_ctrl.dma0_1_eth = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 8, 23, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_B, 24, 25, pinmux_fixed); + hwprot.eth = hwprot.eth_mdio = regk_pinmux_yes; + break; + case pinmux_geth: + ret = crisv32_pinmux_alloc(PORT_B, 0, 7, pinmux_fixed); + hwprot.geth = regk_pinmux_yes; + break; + case pinmux_tg_cmos: + clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 27, 29, pinmux_fixed); + hwprot.tg_clk = regk_pinmux_yes; + break; + case pinmux_tg_ccd: + clk_ctrl.ccd_tg_100 = clk_ctrl.ccd_tg_200 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_B, 27, 31, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 0, 15, pinmux_fixed); + hwprot.tg = hwprot.tg_clk = regk_pinmux_yes; + break; + case pinmux_vout: + clk_ctrl.strdma0_2_video = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 8, 18, pinmux_fixed); + hwprot.vout = hwprot.vout_sync = regk_pinmux_yes; + break; + case pinmux_ser1: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 24, 25, pinmux_fixed); + hwprot.ser1 = regk_pinmux_yes; + break; + case pinmux_ser2: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 26, 27, pinmux_fixed); + hwprot.ser2 = regk_pinmux_yes; + break; + case pinmux_ser3: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 28, 29, pinmux_fixed); + hwprot.ser3 = regk_pinmux_yes; + break; + case pinmux_ser4: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 30, 31, pinmux_fixed); + hwprot.ser4 = regk_pinmux_yes; + break; + case pinmux_sser: + clk_ctrl.sser_ser_dma6_7 = regk_clkgen_yes; + ret = crisv32_pinmux_alloc(PORT_A, 19, 23, pinmux_fixed); + hwprot.sser = regk_pinmux_yes; + break; + case pinmux_pio: + hwprot.pio = regk_pinmux_yes; + ret = 0; + break; + case pinmux_pwm0: + ret = crisv32_pinmux_alloc(PORT_A, 30, 30, pinmux_fixed); + hwprot.pwm0 = regk_pinmux_yes; + break; + case pinmux_pwm1: + ret = crisv32_pinmux_alloc(PORT_A, 31, 31, pinmux_fixed); + hwprot.pwm1 = regk_pinmux_yes; + break; + case pinmux_pwm2: + ret = crisv32_pinmux_alloc(PORT_B, 26, 26, pinmux_fixed); + hwprot.pwm2 = regk_pinmux_yes; + break; + case pinmux_i2c0: + ret = crisv32_pinmux_alloc(PORT_A, 0, 1, pinmux_fixed); + hwprot.i2c0 = regk_pinmux_yes; + break; + case pinmux_i2c1: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + hwprot.i2c1 = regk_pinmux_yes; + break; + case pinmux_i2c1_3wire: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 7, 7, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_yes; + break; + case pinmux_i2c1_sda1: + ret = crisv32_pinmux_alloc(PORT_A, 2, 4, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda1 = regk_pinmux_yes; + break; + case pinmux_i2c1_sda2: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 5, 5, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda2 = regk_pinmux_yes; + break; + case pinmux_i2c1_sda3: + ret = crisv32_pinmux_alloc(PORT_A, 2, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_A, 6, 6, pinmux_fixed); + hwprot.i2c1 = hwprot.i2c1_sda3 = regk_pinmux_yes; + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) { + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + REG_WR(clkgen, regi_clkgen, rw_clk_ctrl, clk_ctrl); + } else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void +crisv32_pinmux_set(int port) +{ + int i; + int gpio_val = 0; + int iop_val = 0; + int pin = port * PORT_PINS; + + for (i = 0; (i < PORT_PINS) && (pin < PINS); i++, pin++) { + if (pins[pin] == pinmux_gpio) + gpio_val |= (1 << i); + else if (pins[pin] == pinmux_iop) + iop_val |= (1 << i); + } + + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_gio_pa + 4 * port, + gpio_val); + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_iop_pa + 4 * port, + iop_val); + +#ifdef DEBUG + crisv32_pinmux_dump(); +#endif +} + +int +crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + pins[port * PORT_PINS + i] = pinmux_none; + + crisv32_pinmux_set(port); + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int +crisv32_pinmux_dealloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_eth: + ret = crisv32_pinmux_dealloc(PORT_B, 8, 23); + ret |= crisv32_pinmux_dealloc(PORT_B, 24, 25); + ret |= crisv32_pinmux_dealloc(PORT_B, 0, 7); + hwprot.eth = hwprot.eth_mdio = hwprot.geth = regk_pinmux_no; + break; + case pinmux_tg_cmos: + ret = crisv32_pinmux_dealloc(PORT_B, 27, 29); + hwprot.tg_clk = regk_pinmux_no; + break; + case pinmux_tg_ccd: + ret = crisv32_pinmux_dealloc(PORT_B, 27, 31); + ret |= crisv32_pinmux_dealloc(PORT_C, 0, 15); + hwprot.tg = hwprot.tg_clk = regk_pinmux_no; + break; + case pinmux_vout: + ret = crisv32_pinmux_dealloc(PORT_A, 8, 18); + hwprot.vout = hwprot.vout_sync = regk_pinmux_no; + break; + case pinmux_ser1: + ret = crisv32_pinmux_dealloc(PORT_A, 24, 25); + hwprot.ser1 = regk_pinmux_no; + break; + case pinmux_ser2: + ret = crisv32_pinmux_dealloc(PORT_A, 26, 27); + hwprot.ser2 = regk_pinmux_no; + break; + case pinmux_ser3: + ret = crisv32_pinmux_dealloc(PORT_A, 28, 29); + hwprot.ser3 = regk_pinmux_no; + break; + case pinmux_ser4: + ret = crisv32_pinmux_dealloc(PORT_A, 30, 31); + hwprot.ser4 = regk_pinmux_no; + break; + case pinmux_sser: + ret = crisv32_pinmux_dealloc(PORT_A, 19, 23); + hwprot.sser = regk_pinmux_no; + break; + case pinmux_pwm0: + ret = crisv32_pinmux_dealloc(PORT_A, 30, 30); + hwprot.pwm0 = regk_pinmux_no; + break; + case pinmux_pwm1: + ret = crisv32_pinmux_dealloc(PORT_A, 31, 31); + hwprot.pwm1 = regk_pinmux_no; + break; + case pinmux_pwm2: + ret = crisv32_pinmux_dealloc(PORT_B, 26, 26); + hwprot.pwm2 = regk_pinmux_no; + break; + case pinmux_i2c0: + ret = crisv32_pinmux_dealloc(PORT_A, 0, 1); + hwprot.i2c0 = regk_pinmux_no; + break; + case pinmux_i2c1: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + hwprot.i2c1 = regk_pinmux_no; + break; + case pinmux_i2c1_3wire: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 7, 7); + hwprot.i2c1 = hwprot.i2c1_sen = regk_pinmux_no; + break; + case pinmux_i2c1_sda1: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 4); + hwprot.i2c1_sda1 = regk_pinmux_no; + break; + case pinmux_i2c1_sda2: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 5, 5); + hwprot.i2c1_sda2 = regk_pinmux_no; + break; + case pinmux_i2c1_sda3: + ret = crisv32_pinmux_dealloc(PORT_A, 2, 3); + ret |= crisv32_pinmux_dealloc(PORT_A, 6, 6); + hwprot.i2c1_sda3 = regk_pinmux_no; + break; + default: + ret = -EINVAL; + break; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void +crisv32_pinmux_dump(void) +{ + int i, j; + int pin = 0; + + crisv32_pinmux_init(); + + for (i = 0; i < PORTS; i++) { + pin++; + printk(KERN_DEBUG "Port %c\n", 'A'+i); + for (j = 0; (j < PORT_PINS) && (pin < PINS); j++, pin++) + printk(KERN_DEBUG + " Pin %d = %d\n", j, pins[i * PORT_PINS + j]); + } +} + +__initcall(crisv32_pinmux_init); diff --git a/arch/cris/arch-v32/mach-a3/vcs_hook.c b/arch/cris/arch-v32/mach-a3/vcs_hook.c new file mode 100644 index 00000000000..58b1a5469fd --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/vcs_hook.c @@ -0,0 +1,103 @@ +/* + * Simulator hook mechanism + */ + +#include "vcs_hook.h" +#include <asm/io.h> +#include <stdarg.h> + +#define HOOK_TRIG_ADDR 0xb7000000 +#define HOOK_MEM_BASE_ADDR 0xce000000 + +static volatile unsigned *hook_base; + +#define HOOK_DATA(offset) hook_base[offset] +#define VHOOK_DATA(offset) hook_base[offset] +#define HOOK_TRIG(funcid) \ + do { \ + *((unsigned *) HOOK_TRIG_ADDR) = funcid; \ + } while (0) +#define HOOK_DATA_BYTE(offset) ((unsigned char *)hook_base)[offset] + +static void hook_init(void) +{ + static int first = 1; + if (first) { + first = 0; + hook_base = ioremap(HOOK_MEM_BASE_ADDR, 8192); + } +} + +static unsigned hook_trig(unsigned id) +{ + unsigned ret; + + /* preempt_disable(); */ + + /* Dummy read from mem to make sure data has propagated to memory + * before trigging */ + ret = *hook_base; + + /* trigger hook */ + HOOK_TRIG(id); + + /* wait for call to finish */ + while (VHOOK_DATA(0) > 0) ; + + /* extract return value */ + + ret = VHOOK_DATA(1); + + return ret; +} + +int hook_call(unsigned id, unsigned pcnt, ...) +{ + va_list ap; + int i; + unsigned ret; + + hook_init(); + + HOOK_DATA(0) = id; + + va_start(ap, pcnt); + for (i = 1; i <= pcnt; i++) + HOOK_DATA(i) = va_arg(ap, unsigned); + va_end(ap); + + ret = hook_trig(id); + + return ret; +} + +int hook_call_str(unsigned id, unsigned size, const char *str) +{ + int i; + unsigned ret; + + hook_init(); + + HOOK_DATA(0) = id; + HOOK_DATA(1) = size; + + for (i = 0; i < size; i++) + HOOK_DATA_BYTE(8 + i) = str[i]; + HOOK_DATA_BYTE(8 + i) = 0; + + ret = hook_trig(id); + + return ret; +} + +void print_str(const char *str) +{ + int i; + /* find null at end of string */ + for (i = 1; str[i]; i++) ; + hook_call(hook_print_str, i, str); +} + +void CPU_WATCHDOG_TIMEOUT(unsigned t) +{ +} diff --git a/arch/cris/arch-v32/mach-a3/vcs_hook.h b/arch/cris/arch-v32/mach-a3/vcs_hook.h new file mode 100644 index 00000000000..8b73d0e8392 --- /dev/null +++ b/arch/cris/arch-v32/mach-a3/vcs_hook.h @@ -0,0 +1,58 @@ +/* + * Simulator hook call mechanism + */ + +#ifndef __hook_h__ +#define __hook_h__ + +int hook_call(unsigned id, unsigned pcnt, ...); +int hook_call_str(unsigned id, unsigned size, const char *str); + +enum hook_ids { + hook_debug_on = 1, + hook_debug_off, + hook_stop_sim_ok, + hook_stop_sim_fail, + hook_alloc_shared, + hook_ptr_shared, + hook_free_shared, + hook_file2shared, + hook_cmp_shared, + hook_print_params, + hook_sim_time, + hook_stop_sim, + hook_kick_dog, + hook_dog_timeout, + hook_rand, + hook_srand, + hook_rand_range, + hook_print_str, + hook_print_hex, + hook_cmp_offset_shared, + hook_fill_random_shared, + hook_alloc_random_data, + hook_calloc_random_data, + hook_print_int, + hook_print_uint, + hook_fputc, + hook_init_fd, + hook_sbrk, + hook_print_context_descr, + hook_print_data_descr, + hook_print_group_descr, + hook_fill_shared, + hook_sl_srand, + hook_sl_rand_irange, + hook_sl_rand_urange, + hook_sl_sh_malloc_aligned, + hook_sl_sh_calloc_aligned, + hook_sl_sh_alloc_random_data, + hook_sl_sh_file2mem, + hook_sl_vera_mbox_handle, + hook_sl_vera_mbox_put, + hook_sl_vera_mbox_get, + hook_sl_system, + hook_sl_sh_hexdump +}; + +#endif diff --git a/arch/cris/arch-v32/mach-fs/Kconfig b/arch/cris/arch-v32/mach-fs/Kconfig new file mode 100644 index 00000000000..f6d74475f1c --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/Kconfig @@ -0,0 +1,216 @@ +if ETRAXFS + +menu "ETRAX FS options" + depends on ETRAXFS + +config ETRAX_DRAM_VIRTUAL_BASE + hex + depends on ETRAX_ARCH_V32 + default "c0000000" + +config ETRAX_SERIAL_PORTS + int + default 4 + +config ETRAX_MEM_GRP1_CONFIG + hex "MEM_GRP1_CONFIG" + depends on ETRAX_ARCH_V32 + default "4044a" + help + Waitstates for flash. The default value is suitable for the + standard flashes used in axis products (120 ns). + +config ETRAX_MEM_GRP2_CONFIG + hex "MEM_GRP2_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for SRAM. 0 is a good choice for most Axis products. + +config ETRAX_MEM_GRP3_CONFIG + hex "MEM_GRP3_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for CSP0-3. 0 is a good choice for most Axis products. + It may need to be changed if external devices such as extra + register-mapped LEDs are used. + +config ETRAX_MEM_GRP4_CONFIG + hex "MEM_GRP4_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + Waitstates for CSP4-6. 0 is a good choice for most Axis products. + +config ETRAX_SDRAM_GRP0_CONFIG + hex "SDRAM_GRP0_CONFIG" + depends on ETRAX_ARCH_V32 + default "336" + help + SDRAM configuration for group 0. The value depends on the + hardware configuration. The default value is suitable + for 32 MB organized as two 16 bits chips (e.g. Axis + part number 18550) connected as one 32 bit device (i.e. in + the same group). + +config ETRAX_SDRAM_GRP1_CONFIG + hex "SDRAM_GRP1_CONFIG" + depends on ETRAX_ARCH_V32 + default "0" + help + SDRAM configuration for group 1. The defult value is 0 + because group 1 is not used in the default configuration, + described in the help for SDRAM_GRP0_CONFIG. + +config ETRAX_SDRAM_TIMING + hex "SDRAM_TIMING" + depends on ETRAX_ARCH_V32 + default "104a" + help + SDRAM timing parameters. The default value is ok for + most hardwares but large SDRAMs may require a faster + refresh (a.k.a 8K refresh). The default value implies + 100MHz clock and SDR mode. + +config ETRAX_SDRAM_COMMAND + hex "SDRAM_COMMAND" + depends on ETRAX_ARCH_V32 + default "0" + help + SDRAM command. Should be 0 unless you really know what + you are doing (may be != 0 for unusual address line + mappings such as in a MCM).. + +config ETRAX_DEF_GIO_PA_OE + hex "GIO_PA_OE" + depends on ETRAX_ARCH_V32 + default "1c" + help + Configures the direction of general port A bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PA_OUT + hex "GIO_PA_OUT" + depends on ETRAX_ARCH_V32 + default "00" + help + Configures the initial data for the general port A bits. Most + products should use 00 here. + +config ETRAX_DEF_GIO_PB_OE + hex "GIO_PB_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port B bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PB_OUT + hex "GIO_PB_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port B bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PC_OE + hex "GIO_PC_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port C bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PC_OUT + hex "GIO_PC_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port C bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PD_OE + hex "GIO_PD_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port D bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PD_OUT + hex "GIO_PD_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port D bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PE_OE + hex "GIO_PE_OE" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the direction of general port E bits. 1 is out, 0 is in. + This is often totally different depending on the product used. + There are some guidelines though - if you know that only LED's are + connected to port PA, then they are usually connected to bits 2-4 + and you can therefore use 1c. On other boards which don't have the + LED's at the general ports, these bits are used for all kinds of + stuff. If you don't know what to use, it is always safe to put all + as inputs, although floating inputs isn't good. + +config ETRAX_DEF_GIO_PE_OUT + hex "GIO_PE_OUT" + depends on ETRAX_ARCH_V32 + default "00000" + help + Configures the initial data for the general port E bits. Most + products should use 00000 here. + +config ETRAX_DEF_GIO_PV_OE + hex "GIO_PV_OE" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the direction of virtual general port V bits. 1 is out, + 0 is in. This is often totally different depending on the product + used. These bits are used for all kinds of stuff. If you don't know + what to use, it is always safe to put all as inputs, although + floating inputs isn't good. + +config ETRAX_DEF_GIO_PV_OUT + hex "GIO_PV_OUT" + depends on ETRAX_VIRTUAL_GPIO + default "0000" + help + Configures the initial data for the virtual general port V bits. + Most products should use 0000 here. + +endmenu + +endif diff --git a/arch/cris/arch-v32/mach-fs/Makefile b/arch/cris/arch-v32/mach-fs/Makefile new file mode 100644 index 00000000000..4ff407a1b93 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/Makefile @@ -0,0 +1,11 @@ +# $Id: Makefile,v 1.3 2007/03/13 11:57:46 starvik Exp $ +# +# Makefile for the linux kernel. +# + +obj-y := dma.o pinmux.o io.o arbiter.o +bj-$(CONFIG_ETRAX_VCS_SIM) += vcs_hook.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o + +clean: + diff --git a/arch/cris/arch-v32/mach-fs/arbiter.c b/arch/cris/arch-v32/mach-fs/arbiter.c new file mode 100644 index 00000000000..84d31bd7b69 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/arbiter.c @@ -0,0 +1,404 @@ +/* + * Memory arbiter functions. Allocates bandwidth through the + * arbiter and sets up arbiter breakpoints. + * + * The algorithm first assigns slots to the clients that has specified + * bandwidth (e.g. ethernet) and then the remaining slots are divided + * on all the active clients. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <arbiter.h> +#include <hwregs/intr_vect.h> +#include <linux/interrupt.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/irq_regs.h> + +struct crisv32_watch_entry { + unsigned long instance; + watch_callback *cb; + unsigned long start; + unsigned long end; + int used; +}; + +#define NUMBER_OF_BP 4 +#define NBR_OF_CLIENTS 14 +#define NBR_OF_SLOTS 64 +#define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ +#define INTMEM_BANDWIDTH 400000000 +#define NBR_OF_REGIONS 2 + +static struct crisv32_watch_entry watches[NUMBER_OF_BP] = { + {regi_marb_bp0}, + {regi_marb_bp1}, + {regi_marb_bp2}, + {regi_marb_bp3} +}; + +static u8 requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static u8 active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; +static int max_bandwidth[NBR_OF_REGIONS] = + { SDRAM_BANDWIDTH, INTMEM_BANDWIDTH }; + +DEFINE_SPINLOCK(arbiter_lock); + +static irqreturn_t crisv32_arbiter_irq(int irq, void *dev_id); + +/* + * "I'm the arbiter, I know the score. + * From square one I'll be watching all 64." + * (memory arbiter slots, that is) + * + * Or in other words: + * Program the memory arbiter slots for "region" according to what's + * in requested_slots[] and active_clients[], while minimizing + * latency. A caller may pass a non-zero positive amount for + * "unused_slots", which must then be the unallocated, remaining + * number of slots, free to hand out to any client. + */ + +static void crisv32_arbiter_config(int region, int unused_slots) +{ + int slot; + int client; + int interval = 0; + + /* + * This vector corresponds to the hardware arbiter slots (see + * the hardware documentation for semantics). We initialize + * each slot with a suitable sentinel value outside the valid + * range {0 .. NBR_OF_CLIENTS - 1} and replace them with + * client indexes. Then it's fed to the hardware. + */ + s8 val[NBR_OF_SLOTS]; + + for (slot = 0; slot < NBR_OF_SLOTS; slot++) + val[slot] = -1; + + for (client = 0; client < NBR_OF_CLIENTS; client++) { + int pos; + /* Allocate the requested non-zero number of slots, but + * also give clients with zero-requests one slot each + * while stocks last. We do the latter here, in client + * order. This makes sure zero-request clients are the + * first to get to any spare slots, else those slots + * could, when bandwidth is allocated close to the limit, + * all be allocated to low-index non-zero-request clients + * in the default-fill loop below. Another positive but + * secondary effect is a somewhat better spread of the + * zero-bandwidth clients in the vector, avoiding some of + * the latency that could otherwise be caused by the + * partitioning of non-zero-bandwidth clients at low + * indexes and zero-bandwidth clients at high + * indexes. (Note that this spreading can only affect the + * unallocated bandwidth.) All the above only matters for + * memory-intensive situations, of course. + */ + if (!requested_slots[region][client]) { + /* + * Skip inactive clients. Also skip zero-slot + * allocations in this pass when there are no known + * free slots. + */ + if (!active_clients[region][client] + || unused_slots <= 0) + continue; + + unused_slots--; + + /* Only allocate one slot for this client. */ + interval = NBR_OF_SLOTS; + } else + interval = + NBR_OF_SLOTS / requested_slots[region][client]; + + pos = 0; + while (pos < NBR_OF_SLOTS) { + if (val[pos] >= 0) + pos++; + else { + val[pos] = client; + pos += interval; + } + } + } + + client = 0; + for (slot = 0; slot < NBR_OF_SLOTS; slot++) { + /* + * Allocate remaining slots in round-robin + * client-number order for active clients. For this + * pass, we ignore requested bandwidth and previous + * allocations. + */ + if (val[slot] < 0) { + int first = client; + while (!active_clients[region][client]) { + client = (client + 1) % NBR_OF_CLIENTS; + if (client == first) + break; + } + val[slot] = client; + client = (client + 1) % NBR_OF_CLIENTS; + } + if (region == EXT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, + val[slot]); + else if (region == INT_REGION) + REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, + val[slot]); + } +} + +extern char _stext, _etext; + +static void crisv32_arbiter_init(void) +{ + static int initialized; + + if (initialized) + return; + + initialized = 1; + + /* + * CPU caches are always set to active, but with zero + * bandwidth allocated. It should be ok to allocate zero + * bandwidth for the caches, because DMA for other channels + * will supposedly finish, once their programmed amount is + * done, and then the caches will get access according to the + * "fixed scheme" for unclaimed slots. Though, if for some + * use-case somewhere, there's a maximum CPU latency for + * e.g. some interrupt, we have to start allocating specific + * bandwidth for the CPU caches too. + */ + active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; + crisv32_arbiter_config(EXT_REGION, 0); + crisv32_arbiter_config(INT_REGION, 0); + + if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED, + "arbiter", NULL)) + printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); + +#ifndef CONFIG_ETRAX_KGDB + /* Global watch for writes to kernel text segment. */ + crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, + arbiter_all_clients, arbiter_all_write, NULL); +#endif +} + +/* Main entry for bandwidth allocation. */ + +int crisv32_arbiter_allocate_bandwidth(int client, int region, + unsigned long bandwidth) +{ + int i; + int total_assigned = 0; + int total_clients = 0; + int req; + + crisv32_arbiter_init(); + + for (i = 0; i < NBR_OF_CLIENTS; i++) { + total_assigned += requested_slots[region][i]; + total_clients += active_clients[region][i]; + } + + /* Avoid division by 0 for 0-bandwidth requests. */ + req = bandwidth == 0 + ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); + + /* + * We make sure that there are enough slots only for non-zero + * requests. Requesting 0 bandwidth *may* allocate slots, + * though if all bandwidth is allocated, such a client won't + * get any and will have to rely on getting memory access + * according to the fixed scheme that's the default when one + * of the slot-allocated clients doesn't claim their slot. + */ + if (total_assigned + req > NBR_OF_SLOTS) + return -ENOMEM; + + active_clients[region][client] = 1; + requested_slots[region][client] = req; + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); + + return 0; +} + +/* + * Main entry for bandwidth deallocation. + * + * Strictly speaking, for a somewhat constant set of clients where + * each client gets a constant bandwidth and is just enabled or + * disabled (somewhat dynamically), no action is necessary here to + * avoid starvation for non-zero-allocation clients, as the allocated + * slots will just be unused. However, handing out those unused slots + * to active clients avoids needless latency if the "fixed scheme" + * would give unclaimed slots to an eager low-index client. + */ + +void crisv32_arbiter_deallocate_bandwidth(int client, int region) +{ + int i; + int total_assigned = 0; + + requested_slots[region][client] = 0; + active_clients[region][client] = 0; + + for (i = 0; i < NBR_OF_CLIENTS; i++) + total_assigned += requested_slots[region][i]; + + crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned); +} + +int crisv32_arbiter_watch(unsigned long start, unsigned long size, + unsigned long clients, unsigned long accesses, + watch_callback *cb) +{ + int i; + + crisv32_arbiter_init(); + + if (start > 0x80000000) { + printk(KERN_ERR "Arbiter: %lX doesn't look like a " + "physical address", start); + return -EFAULT; + } + + spin_lock(&arbiter_lock); + + for (i = 0; i < NUMBER_OF_BP; i++) { + if (!watches[i].used) { + reg_marb_rw_intr_mask intr_mask = + REG_RD(marb, regi_marb, rw_intr_mask); + + watches[i].used = 1; + watches[i].start = start; + watches[i].end = start + size; + watches[i].cb = cb; + + REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, + watches[i].start); + REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, + watches[i].end); + REG_WR_INT(marb_bp, watches[i].instance, rw_op, + accesses); + REG_WR_INT(marb_bp, watches[i].instance, rw_clients, + clients); + + if (i == 0) + intr_mask.bp0 = regk_marb_yes; + else if (i == 1) + intr_mask.bp1 = regk_marb_yes; + else if (i == 2) + intr_mask.bp2 = regk_marb_yes; + else if (i == 3) + intr_mask.bp3 = regk_marb_yes; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + spin_unlock(&arbiter_lock); + + return i; + } + } + spin_unlock(&arbiter_lock); + return -ENOMEM; +} + +int crisv32_arbiter_unwatch(int id) +{ + reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); + + crisv32_arbiter_init(); + + spin_lock(&arbiter_lock); + + if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { + spin_unlock(&arbiter_lock); + return -EINVAL; + } + + memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); + + if (id == 0) + intr_mask.bp0 = regk_marb_no; + else if (id == 1) + intr_mask.bp2 = regk_marb_no; + else if (id == 2) + intr_mask.bp2 = regk_marb_no; + else if (id == 3) + intr_mask.bp3 = regk_marb_no; + + REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); + + spin_unlock(&arbiter_lock); + return 0; +} + +extern void show_registers(struct pt_regs *regs); + +static irqreturn_t crisv32_arbiter_irq(int irq, void *dev_id) +{ + reg_marb_r_masked_intr masked_intr = + REG_RD(marb, regi_marb, r_masked_intr); + reg_marb_bp_r_brk_clients r_clients; + reg_marb_bp_r_brk_addr r_addr; + reg_marb_bp_r_brk_op r_op; + reg_marb_bp_r_brk_first_client r_first; + reg_marb_bp_r_brk_size r_size; + reg_marb_bp_rw_ack ack = { 0 }; + reg_marb_rw_ack_intr ack_intr = { + .bp0 = 1, .bp1 = 1, .bp2 = 1, .bp3 = 1 + }; + struct crisv32_watch_entry *watch; + + if (masked_intr.bp0) { + watch = &watches[0]; + ack_intr.bp0 = regk_marb_yes; + } else if (masked_intr.bp1) { + watch = &watches[1]; + ack_intr.bp1 = regk_marb_yes; + } else if (masked_intr.bp2) { + watch = &watches[2]; + ack_intr.bp2 = regk_marb_yes; + } else if (masked_intr.bp3) { + watch = &watches[3]; + ack_intr.bp3 = regk_marb_yes; + } else { + return IRQ_NONE; + } + + /* Retrieve all useful information and print it. */ + r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); + r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); + r_op = REG_RD(marb_bp, watch->instance, r_brk_op); + r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); + r_size = REG_RD(marb_bp, watch->instance, r_brk_size); + + printk(KERN_INFO "Arbiter IRQ\n"); + printk(KERN_INFO "Clients %X addr %X op %X first %X size %X\n", + REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), + REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); + + REG_WR(marb_bp, watch->instance, rw_ack, ack); + REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); + + printk(KERN_INFO "IRQ occured at %lX\n", get_irq_regs()->erp); + + if (watch->cb) + watch->cb(); + + return IRQ_HANDLED; +} diff --git a/arch/cris/arch-v32/mach-fs/cpufreq.c b/arch/cris/arch-v32/mach-fs/cpufreq.c new file mode 100644 index 00000000000..d57631c0d8d --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/cpufreq.c @@ -0,0 +1,146 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <hwregs/reg_map.h> +#include <asm/arch/hwregs/reg_rdwr.h> +#include <asm/arch/hwregs/config_defs.h> +#include <asm/arch/hwregs/bif_core_defs.h> + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data); + +static struct notifier_block cris_sdram_freq_notifier_block = { + .notifier_call = cris_sdram_freq_notifier +}; + +static struct cpufreq_frequency_table cris_freq_table[] = { + {0x01, 6000}, + {0x02, 200000}, + {0, CPUFREQ_TABLE_END}, +}; + +static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) +{ + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + return clk_ctrl.pll ? 200000 : 6000; +} + +static void cris_freq_set_cpu_state(unsigned int state) +{ + int i; + struct cpufreq_freqs freqs; + reg_config_rw_clk_ctrl clk_ctrl; + clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); + + for_each_possible_cpu(i) { + freqs.old = cris_freq_get_cpu_frequency(i); + freqs.new = cris_freq_table[state].frequency; + freqs.cpu = i; + } + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + local_irq_disable(); + + /* Even though we may be SMP they will share the same clock + * so all settings are made on CPU0. */ + if (cris_freq_table[state].frequency == 200000) + clk_ctrl.pll = 1; + else + clk_ctrl.pll = 0; + REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); + + local_irq_enable(); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); +}; + +static int cris_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]); +} + +static int cris_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, unsigned int relation) +{ + unsigned int newstate = 0; + + if (cpufreq_frequency_table_target + (policy, cris_freq_table, target_freq, relation, &newstate)) + return -EINVAL; + + cris_freq_set_cpu_state(newstate); + + return 0; +} + +static int cris_freq_cpu_init(struct cpufreq_policy *policy) +{ + int result; + + /* cpuinfo and default policy values */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->cpuinfo.transition_latency = 1000000; /* 1ms */ + policy->cur = cris_freq_get_cpu_frequency(0); + + result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table); + if (result) + return (result); + + cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu); + + return 0; +} + +static int cris_freq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *cris_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver cris_freq_driver = { + .get = cris_freq_get_cpu_frequency, + .verify = cris_freq_verify, + .target = cris_freq_target, + .init = cris_freq_cpu_init, + .exit = cris_freq_cpu_exit, + .name = "cris_freq", + .owner = THIS_MODULE, + .attr = cris_freq_attr, +}; + +static int __init cris_freq_init(void) +{ + int ret; + ret = cpufreq_register_driver(&cris_freq_driver); + cpufreq_register_notifier(&cris_sdram_freq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + return ret; +} + +static int +cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, + void *data) +{ + int i; + struct cpufreq_freqs *freqs = data; + if (val == CPUFREQ_PRECHANGE) { + reg_bif_core_rw_sdram_timing timing = + REG_RD(bif_core, regi_bif_core, rw_sdram_timing); + timing.cpd = (freqs->new == 200000 ? 0 : 1); + + if (freqs->new == 200000) + for (i = 0; i < 50000; i++) ; + REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing); + } + return 0; +} + +module_init(cris_freq_init); diff --git a/arch/cris/arch-v32/kernel/dma.c b/arch/cris/arch-v32/mach-fs/dma.c index 570e19128ff..a6acf4e6345 100644 --- a/arch/cris/arch-v32/kernel/dma.c +++ b/arch/cris/arch-v32/mach-fs/dma.c @@ -3,49 +3,54 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <asm/dma.h> -#include <asm/arch/hwregs/reg_map.h> -#include <asm/arch/hwregs/reg_rdwr.h> -#include <asm/arch/hwregs/marb_defs.h> -#include <asm/arch/hwregs/config_defs.h> -#include <asm/arch/hwregs/strmux_defs.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/marb_defs.h> +#include <hwregs/config_defs.h> +#include <hwregs/strmux_defs.h> #include <linux/errno.h> #include <asm/system.h> -#include <asm/arch/arbiter.h> +#include <asm/arch/mach/arbiter.h> static char used_dma_channels[MAX_DMA_CHANNELS]; -static const char * used_dma_channels_users[MAX_DMA_CHANNELS]; +static const char *used_dma_channels_users[MAX_DMA_CHANNELS]; static DEFINE_SPINLOCK(dma_lock); -int crisv32_request_dma(unsigned int dmanr, const char * device_id, - unsigned options, unsigned int bandwidth, +int crisv32_request_dma(unsigned int dmanr, const char *device_id, + unsigned options, unsigned int bandwidth, enum dma_owner owner) { unsigned long flags; reg_config_rw_clk_ctrl clk_ctrl; reg_strmux_rw_cfg strmux_cfg; - if (crisv32_arbiter_allocate_bandwidth(dmanr, - options & DMA_INT_MEM ? INT_REGION : EXT_REGION, - bandwidth)) - return -ENOMEM; + if (crisv32_arbiter_allocate_bandwidth(dmanr, + options & DMA_INT_MEM ? + INT_REGION : EXT_REGION, + bandwidth)) + return -ENOMEM; spin_lock_irqsave(&dma_lock, flags); if (used_dma_channels[dmanr]) { spin_unlock_irqrestore(&dma_lock, flags); if (options & DMA_VERBOSE_ON_ERROR) { - printk("Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]); + printk(KERN_ERR "Failed to request DMA %i for %s, " + "already allocated by %s\n", + dmanr, + device_id, + used_dma_channels_users[dmanr]); } if (options & DMA_PANIC_ON_ERROR) panic("request_dma error!"); + spin_unlock_irqrestore(&dma_lock, flags); return -EBUSY; } clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl); strmux_cfg = REG_RD(strmux, regi_strmux, rw_cfg); - switch(dmanr) - { + switch (dmanr) { case 0: case 1: clk_ctrl.dma01_eth0 = 1; @@ -72,7 +77,9 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, default: spin_unlock_irqrestore(&dma_lock, flags); if (options & DMA_VERBOSE_ON_ERROR) { - printk("Failed to request DMA %i for %s, only 0-%i valid)\n", dmanr, device_id, MAX_DMA_CHANNELS-1); + printk(KERN_ERR "Failed to request DMA %i for %s, " + "only 0-%i valid)\n", + dmanr, device_id, MAX_DMA_CHANNELS - 1); } if (options & DMA_PANIC_ON_ERROR) @@ -80,8 +87,7 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, return -EINVAL; } - switch(owner) - { + switch (owner) { case dma_eth0: if (dmanr == 0) strmux_cfg.dma0 = regk_strmux_eth0; @@ -212,7 +218,7 @@ int crisv32_request_dma(unsigned int dmanr, const char * device_id, used_dma_channels_users[dmanr] = device_id; REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl); REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg); - spin_unlock_irqrestore(&dma_lock,flags); + spin_unlock_irqrestore(&dma_lock, flags); return 0; } diff --git a/arch/cris/arch-v32/lib/dram_init.S b/arch/cris/arch-v32/mach-fs/dram_init.S index 218fbe259ee..6fbad336527 100644 --- a/arch/cris/arch-v32/lib/dram_init.S +++ b/arch/cris/arch-v32/mach-fs/dram_init.S @@ -1,23 +1,22 @@ -/* $Id: dram_init.S,v 1.4 2005/04/24 18:48:32 starvik Exp $ - * +/* * DRAM/SDRAM initialization - alter with care * This file is intended to be included from other assembler files * * Note: This file may not modify r8 or r9 because they are used to * carry information from the decompresser to the kernel * - * Copyright (C) 2000-2003 Axis Communications AB + * Copyright (C) 2000-2007 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik <starvik@axis.com> */ /* Just to be certain the config file is included, we include it here - * explicitly instead of depending on it being included in the file that + * explicitely instead of depending on it being included in the file that * uses this code. */ -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/bif_core_defs_asm.h> ;; WARNING! The registers r8 and r9 are used as parameters carrying ;; information from the decompressor (if the kernel was compressed). @@ -46,7 +45,7 @@ move.d 0x40, $r4 ; Assume 32 bits and CAS latency = 2 move.d CONFIG_ETRAX_SDRAM_TIMING, $r1 - and.d 0x07, $r1 ; Get CAS latency + and.d 0x07, $r1 ; Get CAS latency cmpq 2, $r1 ; CL = 2 ? beq _bw_check nop @@ -80,12 +79,10 @@ _set_timing: subq 1, $r2 ; Issue initialization command sequence - move.d _sdram_commands_start, $r2 - and.d 0x000fffff, $r2 ; Make sure commands are read from flash - move.d _sdram_commands_end, $r3 - and.d 0x000fffff, $r3 + lapc _sdram_commands_start, $r2 + lapc _sdram_commands_end, $r3 1: clear.d $r6 - move.b [$r2+], $r6 ; Load command + move.b [$r2+], $r6 ; Load command or.d $r4, $r6 ; Add calculated mrs move.d $r6, [$r5] ; Write rw_sdram_cmd ; Wait 80 ns between each command diff --git a/arch/cris/arch-v32/lib/hw_settings.S b/arch/cris/arch-v32/mach-fs/hw_settings.S index fff9443513d..8bde93c3621 100644 --- a/arch/cris/arch-v32/lib/hw_settings.S +++ b/arch/cris/arch-v32/mach-fs/hw_settings.S @@ -1,18 +1,16 @@ /* - * $Id: hw_settings.S,v 1.3 2005/04/24 18:36:57 starvik Exp $ - * * This table is used by some tools to extract hardware parameters. * The table should be included in the kernel and the decompressor. * Don't forget to update the tools if you change this table. * - * Copyright (C) 2001 Axis Communications AB + * Copyright (C) 2001-2007 Axis Communications AB * - * Authors: Mikael Starvik (starvik@axis.com) + * Authors: Mikael Starvik <starvik@axis.com> */ -#include <asm/arch/hwregs/asm/reg_map_asm.h> -#include <asm/arch/hwregs/asm/bif_core_defs_asm.h> -#include <asm/arch/hwregs/asm/gio_defs_asm.h> +#include <hwregs/asm/reg_map_asm.h> +#include <hwregs/asm/bif_core_defs_asm.h> +#include <hwregs/asm/gio_defs_asm.h> .ascii "HW_PARAM_MAGIC" ; Magic number .dword 0xc0004000 ; Kernel start address diff --git a/arch/cris/arch-v32/mach-fs/io.c b/arch/cris/arch-v32/mach-fs/io.c new file mode 100644 index 00000000000..a03a3ad3a18 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/io.c @@ -0,0 +1,191 @@ +/* + * Helper functions for I/O pins. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/io.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/hwregs/gio_defs.h> + +#ifndef DEBUG +#define DEBUG(x) +#endif + +struct crisv32_ioport crisv32_ioports[] = { + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pa_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pa_din), + 8 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pb_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pb_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pc_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pc_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pd_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pd_din), + 18 + }, + { + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_oe), + (unsigned long *)REG_ADDR(gio, regi_gio, rw_pe_dout), + (unsigned long *)REG_ADDR(gio, regi_gio, r_pe_din), + 18 + } +}; + +#define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport) + +struct crisv32_iopin crisv32_led_net0_green; +struct crisv32_iopin crisv32_led_net0_red; +struct crisv32_iopin crisv32_led_net1_green; +struct crisv32_iopin crisv32_led_net1_red; +struct crisv32_iopin crisv32_led2_green; +struct crisv32_iopin crisv32_led2_red; +struct crisv32_iopin crisv32_led3_green; +struct crisv32_iopin crisv32_led3_red; + +/* Dummy port used when green LED and red LED is on the same bit */ +static unsigned long io_dummy; +static struct crisv32_ioport dummy_port = { + &io_dummy, + &io_dummy, + &io_dummy, + 18 +}; +static struct crisv32_iopin dummy_led = { + &dummy_port, + 0 +}; + +static int __init crisv32_io_init(void) +{ + int ret = 0; + + u32 i; + + /* Locks *should* be dynamically initialized. */ + for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++) + spin_lock_init(&crisv32_ioports[i].lock); + spin_lock_init(&dummy_port.lock); + + /* Initialize LEDs */ +#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO)) + ret += + crisv32_io_get_name(&crisv32_led_net0_green, + CONFIG_ETRAX_LED_G_NET0); + crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) { + ret += + crisv32_io_get_name(&crisv32_led_net0_red, + CONFIG_ETRAX_LED_R_NET0); + crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out); + } else + crisv32_led_net0_red = dummy_led; +#endif + +#ifdef CONFIG_ETRAX_NBR_LED_GRP_TWO + ret += + crisv32_io_get_name(&crisv32_led_net1_green, + CONFIG_ETRAX_LED_G_NET1); + crisv32_io_set_dir(&crisv32_led_net1_green, crisv32_io_dir_out); + if (strcmp(CONFIG_ETRAX_LED_G_NET1, CONFIG_ETRAX_LED_R_NET1)) { + crisv32_io_get_name(&crisv32_led_net1_red, + CONFIG_ETRAX_LED_R_NET1); + crisv32_io_set_dir(&crisv32_led_net1_red, crisv32_io_dir_out); + } else + crisv32_led_net1_red = dummy_led; +#endif + + ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_V32_LED2G); + ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_V32_LED2R); + ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_V32_LED3G); + ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_V32_LED3R); + + crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out); + crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out); + + return ret; +} + +__initcall(crisv32_io_init); + +int crisv32_io_get(struct crisv32_iopin *iopin, + unsigned int port, unsigned int pin) +{ + if (port > NBR_OF_PORTS) + return -EINVAL; + if (port > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio)) + return -EIO; + DEBUG(printk(KERN_DEBUG "crisv32_io_get: Allocated pin %d on port %d\n", + pin, port)); + + return 0; +} + +int crisv32_io_get_name(struct crisv32_iopin *iopin, const char *name) +{ + int port; + int pin; + + if (toupper(*name) == 'P') + name++; + + if (toupper(*name) < 'A' || toupper(*name) > 'E') + return -EINVAL; + + port = toupper(*name) - 'A'; + name++; + pin = simple_strtoul(name, NULL, 10); + + if (pin < 0 || pin > crisv32_ioports[port].pin_count) + return -EINVAL; + + iopin->bit = 1 << pin; + iopin->port = &crisv32_ioports[port]; + + /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */ + /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */ + if (port != 0 && crisv32_pinmux_alloc(port - 1, pin, pin, pinmux_gpio)) + return -EIO; + + DEBUG(printk(KERN_DEBUG + "crisv32_io_get_name: Allocated pin %d on port %d\n", + pin, port)); + + return 0; +} + +#ifdef CONFIG_PCI +/* PCI I/O access stuff */ +struct cris_io_operations *cris_iops = NULL; +EXPORT_SYMBOL(cris_iops); +#endif diff --git a/arch/cris/arch-v32/mach-fs/pinmux.c b/arch/cris/arch-v32/mach-fs/pinmux.c new file mode 100644 index 00000000000..d722ad9ae62 --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/pinmux.c @@ -0,0 +1,309 @@ +/* + * Allocator for I/O pins. All pins are allocated to GPIO at bootup. + * Unassigned pins and GPIO pins can be allocated to a fixed interface + * or the I/O processor instead. + * + * Copyright (c) 2004-2007 Axis Communications AB. + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <pinmux.h> +#include <hwregs/pinmux_defs.h> + +#undef DEBUG + +#define PORT_PINS 18 +#define PORTS 4 + +static char pins[PORTS][PORT_PINS]; +static DEFINE_SPINLOCK(pinmux_lock); + +static void crisv32_pinmux_set(int port); + +int crisv32_pinmux_init(void) +{ + static int initialized; + + if (!initialized) { + reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa); + initialized = 1; + REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0); + pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = + pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes; + REG_WR(pinmux, regi_pinmux, rw_pa, pa); + crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_D, 0, PORT_PINS - 1, pinmux_gpio); + crisv32_pinmux_alloc(PORT_E, 0, PORT_PINS - 1, pinmux_gpio); + } + + return 0; +} + +int +crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) { + if ((pins[port][i] != pinmux_none) + && (pins[port][i] != pinmux_gpio) + && (pins[port][i] != mode)) { + spin_unlock_irqrestore(&pinmux_lock, flags); +#ifdef DEBUG + panic("Pinmux alloc failed!\n"); +#endif + return -EPERM; + } + } + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = mode; + + crisv32_pinmux_set(port); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int crisv32_pinmux_alloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_ser1: + ret = crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed); + hwprot.ser1 = regk_pinmux_yes; + break; + case pinmux_ser2: + ret = crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed); + hwprot.ser2 = regk_pinmux_yes; + break; + case pinmux_ser3: + ret = crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed); + hwprot.ser3 = regk_pinmux_yes; + break; + case pinmux_sser0: + ret = crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.sser0 = regk_pinmux_yes; + break; + case pinmux_sser1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + hwprot.sser1 = regk_pinmux_yes; + break; + case pinmux_ata0: + ret = crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed); + hwprot.ata0 = regk_pinmux_yes; + break; + case pinmux_ata1: + ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed); + hwprot.ata1 = regk_pinmux_yes; + break; + case pinmux_ata2: + ret = crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata3: + ret = crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed); + hwprot.ata2 = regk_pinmux_yes; + break; + case pinmux_ata: + ret = crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed); + ret |= crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed); + hwprot.ata = regk_pinmux_yes; + break; + case pinmux_eth1: + ret = crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed); + hwprot.eth1 = regk_pinmux_yes; + hwprot.eth1_mgm = regk_pinmux_yes; + break; + case pinmux_timer: + ret = crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed); + hwprot.timer = regk_pinmux_yes; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void crisv32_pinmux_set(int port) +{ + int i; + int gpio_val = 0; + int iop_val = 0; + + for (i = 0; i < PORT_PINS; i++) { + if (pins[port][i] == pinmux_gpio) + gpio_val |= (1 << i); + else if (pins[port][i] == pinmux_iop) + iop_val |= (1 << i); + } + + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_gio + 8 * port, + gpio_val); + REG_WRITE(int, regi_pinmux + REG_RD_ADDR_pinmux_rw_pb_iop + 8 * port, + iop_val); + +#ifdef DEBUG + crisv32_pinmux_dump(); +#endif +} + +int crisv32_pinmux_dealloc(int port, int first_pin, int last_pin) +{ + int i; + unsigned long flags; + + crisv32_pinmux_init(); + + if (port > PORTS) + return -EINVAL; + + spin_lock_irqsave(&pinmux_lock, flags); + + for (i = first_pin; i <= last_pin; i++) + pins[port][i] = pinmux_none; + + crisv32_pinmux_set(port); + spin_unlock_irqrestore(&pinmux_lock, flags); + + return 0; +} + +int crisv32_pinmux_dealloc_fixed(enum fixed_function function) +{ + int ret = -EINVAL; + char saved[sizeof pins]; + unsigned long flags; + + spin_lock_irqsave(&pinmux_lock, flags); + + /* Save internal data for recovery */ + memcpy(saved, pins, sizeof pins); + + crisv32_pinmux_init(); /* Must be done before we read rw_hwprot */ + + reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot); + + switch (function) { + case pinmux_ser1: + ret = crisv32_pinmux_dealloc(PORT_C, 4, 7); + hwprot.ser1 = regk_pinmux_no; + break; + case pinmux_ser2: + ret = crisv32_pinmux_dealloc(PORT_C, 8, 11); + hwprot.ser2 = regk_pinmux_no; + break; + case pinmux_ser3: + ret = crisv32_pinmux_dealloc(PORT_C, 12, 15); + hwprot.ser3 = regk_pinmux_no; + break; + case pinmux_sser0: + ret = crisv32_pinmux_dealloc(PORT_C, 0, 3); + ret |= crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.sser0 = regk_pinmux_no; + break; + case pinmux_sser1: + ret = crisv32_pinmux_dealloc(PORT_D, 0, 4); + hwprot.sser1 = regk_pinmux_no; + break; + case pinmux_ata0: + ret = crisv32_pinmux_dealloc(PORT_D, 5, 7); + ret |= crisv32_pinmux_dealloc(PORT_D, 15, 17); + hwprot.ata0 = regk_pinmux_no; + break; + case pinmux_ata1: + ret = crisv32_pinmux_dealloc(PORT_D, 0, 4); + ret |= crisv32_pinmux_dealloc(PORT_E, 17, 17); + hwprot.ata1 = regk_pinmux_no; + break; + case pinmux_ata2: + ret = crisv32_pinmux_dealloc(PORT_C, 11, 15); + ret |= crisv32_pinmux_dealloc(PORT_E, 3, 3); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata3: + ret = crisv32_pinmux_dealloc(PORT_C, 8, 10); + ret |= crisv32_pinmux_dealloc(PORT_C, 0, 2); + hwprot.ata2 = regk_pinmux_no; + break; + case pinmux_ata: + ret = crisv32_pinmux_dealloc(PORT_B, 0, 15); + ret |= crisv32_pinmux_dealloc(PORT_D, 8, 15); + hwprot.ata = regk_pinmux_no; + break; + case pinmux_eth1: + ret = crisv32_pinmux_dealloc(PORT_E, 0, 17); + hwprot.eth1 = regk_pinmux_no; + hwprot.eth1_mgm = regk_pinmux_no; + break; + case pinmux_timer: + ret = crisv32_pinmux_dealloc(PORT_C, 16, 16); + hwprot.timer = regk_pinmux_no; + spin_unlock_irqrestore(&pinmux_lock, flags); + return ret; + } + + if (!ret) + REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot); + else + memcpy(pins, saved, sizeof pins); + + spin_unlock_irqrestore(&pinmux_lock, flags); + + return ret; +} + +void crisv32_pinmux_dump(void) +{ + int i, j; + + crisv32_pinmux_init(); + + for (i = 0; i < PORTS; i++) { + printk(KERN_DEBUG "Port %c\n", 'B' + i); + for (j = 0; j < PORT_PINS; j++) + printk(KERN_DEBUG " Pin %d = %d\n", j, pins[i][j]); + } +} + +__initcall(crisv32_pinmux_init); diff --git a/arch/cris/arch-v32/mach-fs/vcs_hook.c b/arch/cris/arch-v32/mach-fs/vcs_hook.c new file mode 100644 index 00000000000..593b10f07ef --- /dev/null +++ b/arch/cris/arch-v32/mach-fs/vcs_hook.c @@ -0,0 +1,100 @@ +/* + * Call simulator hook. This is the part running in the + * simulated program. + */ + +#include "vcs_hook.h" +#include <stdarg.h> +#include <asm/arch-v32/hwregs/reg_map.h> +#include <asm/arch-v32/hwregs/intr_vect_defs.h> + +#define HOOK_TRIG_ADDR 0xb7000000 /* hook cvlog model reg address */ +#define HOOK_MEM_BASE_ADDR 0xa0000000 /* csp4 (shared mem) base addr */ + +#define HOOK_DATA(offset) ((unsigned *)HOOK_MEM_BASE_ADDR)[offset] +#define VHOOK_DATA(offset) ((volatile unsigned *)HOOK_MEM_BASE_ADDR)[offset] +#define HOOK_TRIG(funcid) \ + do { \ + *((unsigned *) HOOK_TRIG_ADDR) = funcid; \ + } while (0) +#define HOOK_DATA_BYTE(offset) ((unsigned char *)HOOK_MEM_BASE_ADDR)[offset] + +int hook_call(unsigned id, unsigned pcnt, ...) +{ + va_list ap; + unsigned i; + unsigned ret; +#ifdef USING_SOS + PREEMPT_OFF_SAVE(); +#endif + + /* pass parameters */ + HOOK_DATA(0) = id; + + /* Have to make hook_print_str a special case since we call with a + * parameter of byte type. Should perhaps be a separate + * hook_call. */ + + if (id == hook_print_str) { + int i; + char *str; + + HOOK_DATA(1) = pcnt; + + va_start(ap, pcnt); + str = (char *)va_arg(ap, unsigned); + + for (i = 0; i != pcnt; i++) + HOOK_DATA_BYTE(8 + i) = str[i]; + + HOOK_DATA_BYTE(8 + i) = 0; /* null byte */ + } else { + va_start(ap, pcnt); + for (i = 1; i <= pcnt; i++) + HOOK_DATA(i) = va_arg(ap, unsigned); + va_end(ap); + } + + /* read from mem to make sure data has propagated to memory before + * trigging */ + ret = *((volatile unsigned *)HOOK_MEM_BASE_ADDR); + + /* trigger hook */ + HOOK_TRIG(id); + + /* wait for call to finish */ + while (VHOOK_DATA(0) > 0) ; + + /* extract return value */ + + ret = VHOOK_DATA(1); + +#ifdef USING_SOS + PREEMPT_RESTORE(); +#endif + return ret; +} + +unsigned hook_buf(unsigned i) +{ + return (HOOK_DATA(i)); +} + +void print_str(const char *str) +{ + int i; + /* find null at end of string */ + for (i = 1; str[i]; i++) ; + hook_call(hook_print_str, i, str); +} + +void CPU_KICK_DOG(void) +{ + (void)hook_call(hook_kick_dog, 0); +} + +void CPU_WATCHDOG_TIMEOUT(unsigned t) +{ + (void)hook_call(hook_dog_timeout, 1, t); +} + diff --git a/arch/cris/arch-v32/kernel/vcs_hook.h b/arch/cris/arch-v32/mach-fs/vcs_hook.h index 7d73709e3cc..c000b9fece4 100644 --- a/arch/cris/arch-v32/kernel/vcs_hook.h +++ b/arch/cris/arch-v32/mach-fs/vcs_hook.h @@ -1,11 +1,11 @@ -// $Id: vcs_hook.h,v 1.1 2003/08/12 12:01:06 starvik Exp $ -// -// Call simulator hook functions +/* + * Call simulator hook functions + */ #ifndef HOOK_H #define HOOK_H -int hook_call( unsigned id, unsigned pcnt, ...); +int hook_call(unsigned id, unsigned pcnt, ...); enum hook_ids { hook_debug_on = 1, diff --git a/arch/cris/arch-v32/mm/Makefile b/arch/cris/arch-v32/mm/Makefile index 9146f88484b..0b801f2964a 100644 --- a/arch/cris/arch-v32/mm/Makefile +++ b/arch/cris/arch-v32/mm/Makefile @@ -1,3 +1,4 @@ # Makefile for the Linux/cris parts of the memory manager. -obj-y := mmu.o init.o tlb.o intmem.o +obj-y += mmu.o init.o tlb.o intmem.o +obj-$(CONFIG_ETRAX_L2CACHE) += l2cache.o diff --git a/arch/cris/arch-v32/mm/init.c b/arch/cris/arch-v32/mm/init.c index a84ba7ff22d..5a9ac583464 100644 --- a/arch/cris/arch-v32/mm/init.c +++ b/arch/cris/arch-v32/mm/init.c @@ -65,7 +65,7 @@ cris_mmu_init(void) REG_STATE(mmu, rw_mm_cfg, seg_d, page) | REG_STATE(mmu, rw_mm_cfg, seg_c, linear) | REG_STATE(mmu, rw_mm_cfg, seg_b, linear) | -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM REG_STATE(mmu, rw_mm_cfg, seg_a, page) | #else REG_STATE(mmu, rw_mm_cfg, seg_a, linear) | @@ -84,13 +84,9 @@ cris_mmu_init(void) mmu_kbase_hi = ( REG_FIELD(mmu, rw_mm_kbase_hi, base_f, 0x0) | REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 0x8) | REG_FIELD(mmu, rw_mm_kbase_hi, base_d, 0x0) | -#ifndef CONFIG_ETRAXFS_SIM REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x4) | -#else - REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0x0) | -#endif REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb) | -#ifndef CONFIG_ETRAXFS_SIM +#ifndef CONFIG_ETRAX_VCS_SIM REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0x0) | #else REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa) | diff --git a/arch/cris/arch-v32/mm/intmem.c b/arch/cris/arch-v32/mm/intmem.c index 41ee7f7997f..9e8b69cdf19 100644 --- a/arch/cris/arch-v32/mm/intmem.c +++ b/arch/cris/arch-v32/mm/intmem.c @@ -7,11 +7,17 @@ #include <linux/list.h> #include <linux/slab.h> #include <asm/io.h> -#include <asm/arch/memmap.h> +#include <memmap.h> #define STATUS_FREE 0 #define STATUS_ALLOCATED 1 +#ifdef CONFIG_ETRAX_L2CACHE +#define RESERVED_SIZE 66*1024 +#else +#define RESERVED_SIZE 0 +#endif + struct intmem_allocation { struct list_head entry; unsigned int size; @@ -30,9 +36,10 @@ static void crisv32_intmem_init(void) struct intmem_allocation* alloc = (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL); INIT_LIST_HEAD(&intmem_allocations); - intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE); + intmem_virtual = ioremap(MEM_INTMEM_START + RESERVED_SIZE, + MEM_INTMEM_SIZE - RESERVED_SIZE); initiated = 1; - alloc->size = MEM_INTMEM_SIZE; + alloc->size = MEM_INTMEM_SIZE - RESERVED_SIZE; alloc->offset = 0; alloc->status = STATUS_FREE; list_add_tail(&alloc->entry, &intmem_allocations); @@ -59,19 +66,23 @@ void* crisv32_intmem_alloc(unsigned size, unsigned align) (struct intmem_allocation*) kmalloc(sizeof *alloc, GFP_ATOMIC); alloc->status = STATUS_FREE; - alloc->size = allocation->size - size - alignment; - alloc->offset = allocation->offset + size; + alloc->size = allocation->size - size - + alignment; + alloc->offset = allocation->offset + size + + alignment; list_add(&alloc->entry, &allocation->entry); if (alignment) { - struct intmem_allocation* tmp; - tmp = (struct intmem_allocation*) - kmalloc(sizeof *tmp, GFP_ATOMIC); + struct intmem_allocation *tmp; + tmp = (struct intmem_allocation *) + kmalloc(sizeof *tmp, + GFP_ATOMIC); tmp->offset = allocation->offset; tmp->size = alignment; tmp->status = STATUS_FREE; allocation->offset += alignment; - list_add_tail(&tmp->entry, &allocation->entry); + list_add_tail(&tmp->entry, + &allocation->entry); } } allocation->status = STATUS_ALLOCATED; @@ -96,22 +107,24 @@ void crisv32_intmem_free(void* addr) list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) { if (allocation->offset == (int)(addr - intmem_virtual)) { - struct intmem_allocation* prev = + struct intmem_allocation *prev = list_entry(allocation->entry.prev, struct intmem_allocation, entry); - struct intmem_allocation* next = + struct intmem_allocation *next = list_entry(allocation->entry.next, struct intmem_allocation, entry); allocation->status = STATUS_FREE; /* Join with prev and/or next if also free */ - if (prev->status == STATUS_FREE) { + if ((prev != &intmem_allocations) && + (prev->status == STATUS_FREE)) { prev->size += allocation->size; list_del(&allocation->entry); kfree(allocation); allocation = prev; } - if (next->status == STATUS_FREE) { + if ((next != &intmem_allocations) && + (next->status == STATUS_FREE)) { allocation->size += next->size; list_del(&next->entry); kfree(next); @@ -125,15 +138,16 @@ void crisv32_intmem_free(void* addr) void* crisv32_intmem_phys_to_virt(unsigned long addr) { - return (void*)(addr - MEM_INTMEM_START+ - (unsigned long)intmem_virtual); + return (void *)(addr - (MEM_INTMEM_START + RESERVED_SIZE) + + (unsigned long)intmem_virtual); } unsigned long crisv32_intmem_virt_to_phys(void* addr) { return (unsigned long)((unsigned long )addr - - (unsigned long)intmem_virtual + MEM_INTMEM_START); + (unsigned long)intmem_virtual + MEM_INTMEM_START + + RESERVED_SIZE); } - +module_init(crisv32_intmem_init); diff --git a/arch/cris/arch-v32/mm/l2cache.c b/arch/cris/arch-v32/mm/l2cache.c new file mode 100644 index 00000000000..332ff10dcc6 --- /dev/null +++ b/arch/cris/arch-v32/mm/l2cache.c @@ -0,0 +1,29 @@ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <memmap.h> +#include <hwregs/reg_map.h> +#include <hwregs/reg_rdwr.h> +#include <hwregs/l2cache_defs.h> +#include <asm/io.h> + +#define L2CACHE_SIZE 64 + +int __init l2cache_init(void) +{ + reg_l2cache_rw_ctrl ctrl = {0}; + reg_l2cache_rw_cfg cfg = {.en = regk_l2cache_yes}; + + ctrl.csize = L2CACHE_SIZE; + ctrl.cbase = L2CACHE_SIZE / 4 + (L2CACHE_SIZE % 4 ? 1 : 0); + REG_WR(l2cache, regi_l2cache, rw_ctrl, ctrl); + + /* Flush the tag memory */ + memset((void *)(MEM_INTMEM_START | MEM_NON_CACHEABLE), 0, 2*1024); + + /* Enable the cache */ + REG_WR(l2cache, regi_l2cache, rw_cfg, cfg); + + return 0; +} + diff --git a/arch/cris/arch-v32/mm/mmu.S b/arch/cris/arch-v32/mm/mmu.S index 27b70e5006a..2238d154bde 100644 --- a/arch/cris/arch-v32/mm/mmu.S +++ b/arch/cris/arch-v32/mm/mmu.S @@ -1,3 +1,5 @@ +; WARNING : The refill handler has been modified, see below !!! + /* * Copyright (C) 2003 Axis Communications AB * @@ -61,6 +63,14 @@ ; Note that the code is optimized to minimize stalls (makes the code harder ; to read). ; +; WARNING !!! +; Modified by Mikael Asker 060725: added a workaround for strange TLB +; behavior. If the same PTE is present in more than one set, the TLB +; doesn't recognize it and we get stuck in a loop of refill exceptions. +; The workaround detects such loops and exits them by flushing +; the TLB contents. The problem and workaround were verified +; in VCS by Mikael Starvik. +; ; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each ; PMD holds 16 MB of virtual memory. ; Bits 0-12 : Offset within a page @@ -68,6 +78,11 @@ ; Bits 24-31 : PMD offset within the PGD .macro MMU_REFILL_HANDLER handler, mmu + .data +1: .dword 0 ; refill_count + ; == 0 <=> last_refill_cause is invalid +2: .dword 0 ; last_refill_cause + .text .globl \handler \handler: subq 4, $sp @@ -76,42 +91,96 @@ subq 4, $sp move \mmu, $srs ; Select MMU support register bank move.d $acr, [$sp] - subq 4, $sp - move.d $r0, [$sp] + subq 12, $sp + move.d 1b, $acr ; Point to refill_count + movem $r2, [$sp] + + test.d [$acr] ; refill_count == 0 ? + beq 5f ; yes, last_refill_cause is invalid + move.d $acr, $r1 + + ; last_refill_cause is valid, investigate cause + addq 4, $r1 ; Point to last_refill_cause + move $s3, $r0 ; Get rw_mm_cause + move.d [$r1], $r2 ; Get last_refill_cause + cmp.d $r0, $r2 ; rw_mm_cause == last_refill_cause ? + beq 6f ; yes, increment count + moveq 1, $r2 + + ; rw_mm_cause != last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move.d $r0, [$r1] ; last_refill_cause = rw_mm_cause + +3: ; Probably not in a loop, continue normal processing #ifdef CONFIG_SMP move $s7, $acr ; PGD #else move.d per_cpu__current_pgd, $acr ; PGD #endif ; Look up PMD in PGD - move $s3, $r0 ; rw_mm_cause lsrq 24, $r0 ; Get PMD index into PGD (bit 24-31) move.d [$acr], $acr ; PGD for the current process addi $r0.d, $acr, $acr move $s3, $r0 ; rw_mm_cause move.d [$acr], $acr ; Get PMD - beq 1f + beq 8f ; Look up PTE in PMD lsrq PAGE_SHIFT, $r0 and.w PAGE_MASK, $acr ; Remove PMD flags and.d 0x7ff, $r0 ; Get PTE index into PMD (bit 13-23) addi $r0.d, $acr, $acr move.d [$acr], $acr ; Get PTE - beq 2f - move.d [$sp+], $r0 ; Pop r0 in delayslot + beq 9f + movem [$sp], $r2 ; Restore r0-r2 in delay slot + addq 12, $sp ; Store in TLB move $acr, $s5 - ; Return +4: ; Return move.d [$sp+], $acr - move [$sp], $srs + move [$sp], $srs addq 4, $sp rete rfe -1: ; PMD missing, let the mm subsystem fix it up. - move.d [$sp+], $r0 ; Pop r0 -2: ; PTE missing, let the mm subsystem fix it up. + +5: ; last_refill_cause is invalid + moveq 1, $r2 + addq 4, $r1 ; Point to last_refill_cause + move.d $r2, [$acr] ; refill_count = 1 + move $s3, $r0 ; Get rw_mm_cause + ba 3b ; Continue normal processing + move.d $r0,[$r1] ; last_refill_cause = rw_mm_cause + +6: ; rw_mm_cause == last_refill_cause + move.d [$acr], $r2 ; Get refill_count + cmpq 4, $r2 ; refill_count > 4 ? + bhi 7f ; yes + addq 1, $r2 ; refill_count++ + ba 3b ; Continue normal processing + move.d $r2, [$acr] + +7: ; refill_count > 4, error + move.d $acr, $r0 ; Save pointer to refill_count + clear.d [$r0] ; refill_count = 0 + + ;; rewind the short stack + movem [$sp], $r2 ; Restore r0-r2 + addq 12, $sp + move.d [$sp+], $acr + move [$sp], $srs + addq 4, $sp + ;; Keep it simple (slow), save all the regs. + SAVE_ALL + jsr __flush_tlb_all + nop + ba ret_from_intr ; Return + nop + +8: ; PMD missing, let the mm subsystem fix it up. + movem [$sp], $r2 ; Restore r0-r2 +9: ; PTE missing, let the mm subsystem fix it up. + addq 12, $sp move.d [$sp+], $acr - move [$sp], $srs + move [$sp], $srs addq 4, $sp SAVE_ALL move \mmu, $srs diff --git a/arch/cris/arch-v32/mm/tlb.c b/arch/cris/arch-v32/mm/tlb.c index a076ef6e938..eda5ebcaea5 100644 --- a/arch/cris/arch-v32/mm/tlb.c +++ b/arch/cris/arch-v32/mm/tlb.c @@ -13,8 +13,8 @@ #include <asm/arch/hwregs/supp_reg.h> #define UPDATE_TLB_SEL_IDX(val) \ -do { \ - unsigned long tlb_sel; \ +do { \ + unsigned long tlb_sel; \ \ tlb_sel = REG_FIELD(mmu, rw_mm_tlb_sel, idx, val); \ SUPP_REG_WR(RW_MM_TLB_SEL, tlb_sel); \ @@ -30,8 +30,8 @@ do { \ * The TLB can host up to 256 different mm contexts at the same time. The running * context is found in the PID register. Each TLB entry contains a page_id that * has to match the PID register to give a hit. page_id_map keeps track of which - * mm is assigned to which page_id, making sure it's known when to invalidate TLB - * entries. + * mm's is assigned to which page_id's, making sure it's known when to + * invalidate TLB entries. * * The last page_id is never running, it is used as an invalid page_id so that * it's possible to make TLB entries that will nerver match. @@ -179,29 +179,29 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { - int cpu = smp_processor_id(); - - /* Make sure there is a MMU context. */ - spin_lock(&mmu_context_lock); - get_mmu_context(next); - cpu_set(cpu, next->cpu_vm_mask); - spin_unlock(&mmu_context_lock); - - /* - * Remember the pgd for the fault handlers. Keep a separate copy of it - * because current and active_mm might be invalid at points where - * there's still a need to derefer the pgd. - */ - per_cpu(current_pgd, cpu) = next->pgd; - - /* Switch context in the MMU. */ - if (tsk && task_thread_info(tsk)) - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls); - } - else - { - SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); - } + if (prev != next) { + int cpu = smp_processor_id(); + + /* Make sure there is a MMU context. */ + spin_lock(&mmu_context_lock); + get_mmu_context(next); + cpu_set(cpu, next->cpu_vm_mask); + spin_unlock(&mmu_context_lock); + + /* + * Remember the pgd for the fault handlers. Keep a seperate + * copy of it because current and active_mm might be invalid + * at points where * there's still a need to derefer the pgd. + */ + per_cpu(current_pgd, cpu) = next->pgd; + + /* Switch context in the MMU. */ + if (tsk && task_thread_info(tsk)) { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | + task_thread_info(tsk)->tls); + } else { + SPEC_REG_WR(SPEC_REG_PID, next->context.page_id); + } + } } diff --git a/arch/cris/arch-v32/vmlinux.lds.S b/arch/cris/arch-v32/vmlinux.lds.S index fead8c59ea6..d5f28e40717 100644 --- a/arch/cris/arch-v32/vmlinux.lds.S +++ b/arch/cris/arch-v32/vmlinux.lds.S @@ -9,6 +9,13 @@ */ #include <asm-generic/vmlinux.lds.h> +#include <asm/page.h> + +#ifdef CONFIG_ETRAX_VMEM_SIZE +#define __CONFIG_ETRAX_VMEM_SIZE CONFIG_ETRAX_VMEM_SIZE +#else +#define __CONFIG_ETRAX_VMEM_SIZE 0 +#endif jiffies = jiffies_64; SECTIONS @@ -17,18 +24,19 @@ SECTIONS dram_start = .; ebp_start = .; - /* The boot section is only necessary until the VCS top level testbench */ - /* includes both flash and DRAM. */ + /* The boot section is only necessary until the VCS top */ + /* level testbench includes both flash and DRAM. */ .boot : { *(.boot) } - . = DRAM_VIRTUAL_BASE + 0x4000; /* See head.S and pages reserved at the start. */ + /* See head.S and pages reserved at the start. */ + . = DRAM_VIRTUAL_BASE + 0x4000; _text = .; /* Text and read-only data. */ text_start = .; /* Lots of aliases. */ _stext = .; __stext = .; .text : { - *(.text) + TEXT_TEXT SCHED_TEXT LOCK_TEXT *(.fixup) @@ -39,9 +47,9 @@ SECTIONS __etext = .; . = ALIGN(4); /* Exception table. */ - __start___ex_table = .; - __ex_table : { *(__ex_table) } - __stop___ex_table = .; + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; RODATA @@ -54,33 +62,27 @@ SECTIONS __edata = . ; /* End of data section. */ _edata = . ; - . = ALIGN(8192); /* init_task and stack, must be aligned. */ - .data.init_task : { *(.data.init_task) } + . = ALIGN(PAGE_SIZE); /* init_task and stack, must be aligned. */ + .data.init_task : { *(.data.init_task) } - . = ALIGN(8192); /* Init code and data. */ - __init_begin = .; + . = ALIGN(PAGE_SIZE); /* Init code and data. */ + __init_begin = .; .init.text : { _sinittext = .; INIT_TEXT _einittext = .; } .init.data : { INIT_DATA } - . = ALIGN(16); - __setup_start = .; - .init.setup : { *(.init.setup) } - __setup_end = .; + . = ALIGN(16); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; __start___param = .; __param : { *(__param) } __stop___param = .; - .initcall.init : { + .initcall.init : { __initcall_start = .; - *(.initcall1.init); - *(.initcall2.init); - *(.initcall3.init); - *(.initcall4.init); - *(.initcall5.init); - *(.initcall6.init); - *(.initcall7.init); + INITCALLS __initcall_end = .; } @@ -91,25 +93,23 @@ SECTIONS } SECURITY_INIT - PERCPU(8192) + __vmlinux_end = .; /* Last address of the physical file. */ + PERCPU(PAGE_SIZE) -#ifdef CONFIG_BLK_DEV_INITRD .init.ramfs : { __initramfs_start = .; *(.init.ramfs) __initramfs_end = .; - /* - * We fill to the next page, so we can discard all init - * pages without needing to consider what payload might be - * appended to the kernel image. - */ - FILL (0); - . = ALIGN (8192); } -#endif - __vmlinux_end = .; /* Last address of the physical file. */ - __init_end = .; + /* + * We fill to the next page, so we can discard all init + * pages without needing to consider what payload might be + * appended to the kernel image. + */ + . = ALIGN (PAGE_SIZE); + + __init_end = .; __data_end = . ; /* Move to _edata? */ __bss_start = .; /* BSS. */ @@ -123,11 +123,11 @@ SECTIONS __end = .; /* Sections to be discarded */ - /DISCARD/ : { + /DISCARD/ : { EXIT_TEXT EXIT_DATA *(.exitcall.exit) } - dram_end = dram_start + CONFIG_ETRAX_DRAM_SIZE*1024*1024; + dram_end = dram_start + (CONFIG_ETRAX_DRAM_SIZE - __CONFIG_ETRAX_VMEM_SIZE)*1024*1024; } diff --git a/arch/cris/artpec_3_defconfig b/arch/cris/artpec_3_defconfig new file mode 100644 index 00000000000..41fe67409aa --- /dev/null +++ b/arch/cris/artpec_3_defconfig @@ -0,0 +1,582 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24-rc3 +# Mon Dec 3 11:18:54 2007 +# +CONFIG_MMU=y +CONFIG_ZONE_DMA=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NO_IOPORT=y +CONFIG_FORCE_MAX_ZONEORDER=6 +CONFIG_CRIS=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" + +# +# General setup +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_ETRAX_CMDLINE="root=/dev/mtdblock3 init=/linuxrc" +# CONFIG_ETRAX_WATCHDOG is not set +CONFIG_ETRAX_FAST_TIMER=y +# CONFIG_ETRAX_KMALLOCED_MODULES is not set +# CONFIG_OOM_REBOOT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y + +# +# Hardware setup +# +# CONFIG_ETRAX100LX is not set +# CONFIG_ETRAX100LX_V2 is not set +# CONFIG_SVINTO_SIM is not set +# CONFIG_ETRAXFS is not set +CONFIG_CRIS_MACH_ARTPEC3=y +# CONFIG_ETRAX_VCS_SIM is not set +# CONFIG_ETRAX_ARCH_V10 is not set +CONFIG_ETRAX_ARCH_V32=y +CONFIG_ETRAX_DRAM_SIZE=32 +CONFIG_ETRAX_VMEM_SIZE=8 +CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1 +CONFIG_ETRAX_FLASH1_SIZE=4 +CONFIG_ETRAX_DEBUG_PORT0=y +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +# CONFIG_ETRAX_DEBUG_PORT_NULL is not set +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 +CONFIG_ETRAX_SERIAL_PORTS=5 +CONFIG_ETRAX_DEF_GIO_PA_OE=1c +CONFIG_ETRAX_DEF_GIO_PA_OUT=00 +CONFIG_ETRAX_DEF_GIO_PB_OE=00000 +CONFIG_ETRAX_DEF_GIO_PB_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PC_OE=00000 +CONFIG_ETRAX_DEF_GIO_PC_OUT=00000 + +# +# Artpec-3 options +# +CONFIG_ETRAX_L2CACHE=y +CONFIG_ETRAX_DDR=y +CONFIG_ETRAX_DDR2_MRS=0 +CONFIG_ETRAX_DDR2_TIMING=0 +CONFIG_ETRAX_DDR2_CONFIG=0 +CONFIG_ETRAX_PIO_CE0_CFG=0 +CONFIG_ETRAX_PIO_CE1_CFG=0 +CONFIG_ETRAX_PIO_CE2_CFG=0 +# CONFIG_CPU_FREQ is not set +# CONFIG_ETRAX_NBR_LED_GRP_ZERO is not set +CONFIG_ETRAX_NBR_LED_GRP_ONE=y +# CONFIG_ETRAX_NBR_LED_GRP_TWO is not set +CONFIG_ETRAX_LED_G_NET0="PA3" +CONFIG_ETRAX_LED_R_NET0="PA4" +CONFIG_ETRAX_V32_LED2G="PA5" +CONFIG_ETRAX_V32_LED2R="PA6" +CONFIG_ETRAX_V32_LED3G="PA7" +CONFIG_ETRAX_V32_LED3R="PA7" + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NF_CONNTRACK_ENABLED is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Drivers for built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +# CONFIG_ETRAX_IDE is not set +CONFIG_ETRAX_AXISFLASHMAP=y +CONFIG_ETRAX_PTABLE_SECTOR=65536 +# CONFIG_ETRAX_I2C is not set +# CONFIG_ETRAX_GPIO is not set +# CONFIG_ETRAX_NO_PHY is not set +# CONFIG_ETRAX_ETHERNET_IFACE0 is not set +# CONFIG_ETRAX_ETHERNET_GBIT is not set +# CONFIG_ETRAXFS_SERIAL is not set +# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set +# CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE is not set +# CONFIG_ETRAX_NANDFLASH is not set +# CONFIG_ETRAX_CARDBUS is not set +# CONFIG_ETRAX_IOP_FW_LOAD is not set +# CONFIG_ETRAX_STREAMCOPROC is not set +# CONFIG_ETRAX_SPI_MMC is not set +# CONFIG_ETRAX_SPI_MMC_BOARD is not set +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_RAM=y +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=y +CONFIG_MTDRAM_TOTAL_SIZE=0 +CONFIG_MTDRAM_ERASE_SIZE=64 +CONFIG_MTDRAM_ABS_POS=0x0 +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_RTC_CLASS is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_HW_RANDOM=y +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=y +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PROFILING is not set +# CONFIG_SYSTEM_PROFILER is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SAMPLES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_DMA=y diff --git a/arch/cris/defconfig b/arch/cris/defconfig index 9c33ae65993..59f36a58f84 100644 --- a/arch/cris/defconfig +++ b/arch/cris/defconfig @@ -1,52 +1,91 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.11 -# Mon Jun 20 13:42:02 2005 +# Linux kernel version: 2.6.24-rc3 +# Mon Dec 3 11:34:27 2007 # CONFIG_MMU=y -CONFIG_UID16=y +CONFIG_ZONE_DMA=y CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_IOMAP=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NO_IOPORT=y +CONFIG_FORCE_MAX_ZONEORDER=6 CONFIG_CRIS=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" # -# Code maturity level options +# General setup # CONFIG_EXPERIMENTAL=y -CONFIG_CLEAN_COMPILE=y CONFIG_BROKEN_ON_SMP=y - -# -# General setup -# +CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y # CONFIG_SWAP is not set # CONFIG_SYSVIPC is not set # CONFIG_POSIX_MQUEUE is not set # CONFIG_BSD_PROCESS_ACCT is not set -CONFIG_SYSCTL=y +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set # CONFIG_AUDIT is not set -CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_HOTPLUG is not set -CONFIG_KOBJECT_UEVENT=y # CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y # CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y CONFIG_FUTEX=y +CONFIG_ANON_INODES=y CONFIG_EPOLL=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y CONFIG_SHMEM=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y # CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set # -# Loadable module support +# IO Schedulers # -# CONFIG_MODULES is not set +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" # # General setup @@ -54,12 +93,27 @@ CONFIG_CC_ALIGN_JUMPS=0 CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set CONFIG_GENERIC_HARDIRQS=y -# CONFIG_SMP is not set CONFIG_ETRAX_CMDLINE="root=/dev/mtdblock3 init=/linuxrc" # CONFIG_ETRAX_WATCHDOG is not set CONFIG_ETRAX_FAST_TIMER=y -# CONFIG_PREEMPT is not set +# CONFIG_ETRAX_KMALLOCED_MODULES is not set # CONFIG_OOM_REBOOT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y # # Hardware setup @@ -68,127 +122,180 @@ CONFIG_ETRAX_FAST_TIMER=y CONFIG_ETRAX100LX_V2=y # CONFIG_SVINTO_SIM is not set # CONFIG_ETRAXFS is not set -# CONFIG_ETRAXFS_SIM is not set +# CONFIG_CRIS_MACH_ARTPEC3 is not set +# CONFIG_ETRAX_VCS_SIM is not set CONFIG_ETRAX_ARCH_V10=y # CONFIG_ETRAX_ARCH_V32 is not set CONFIG_ETRAX_DRAM_SIZE=32 CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1 CONFIG_ETRAX_FLASH1_SIZE=4 +# CONFIG_ETRAX_DEBUG_PORT0 is not set +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +CONFIG_ETRAX_DEBUG_PORT_NULL=y + +# +# CRIS v10 options +# CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 CONFIG_ETRAX_PA_LEDS=y # CONFIG_ETRAX_PB_LEDS is not set # CONFIG_ETRAX_CSP0_LEDS is not set # CONFIG_ETRAX_NO_LEDS is not set CONFIG_ETRAX_LED1G=2 -CONFIG_ETRAX_LED1R=2 -CONFIG_ETRAX_LED2G=3 -CONFIG_ETRAX_LED2R=3 +CONFIG_ETRAX_LED1R=3 +CONFIG_ETRAX_LED2G=4 +CONFIG_ETRAX_LED2R=5 CONFIG_ETRAX_LED3G=2 CONFIG_ETRAX_LED3R=2 -CONFIG_ETRAX_DEBUG_PORT0=y -# CONFIG_ETRAX_DEBUG_PORT1 is not set -# CONFIG_ETRAX_DEBUG_PORT2 is not set -# CONFIG_ETRAX_DEBUG_PORT3 is not set -# CONFIG_ETRAX_DEBUG_PORT_NULL is not set CONFIG_ETRAX_RESCUE_SER0=y # CONFIG_ETRAX_RESCUE_SER1 is not set # CONFIG_ETRAX_RESCUE_SER2 is not set # CONFIG_ETRAX_RESCUE_SER3 is not set -CONFIG_ETRAX_DEF_R_WAITSTATES=0x95a6 -CONFIG_ETRAX_DEF_R_BUS_CONFIG=0x4 -CONFIG_ETRAX_SDRAM=y -CONFIG_ETRAX_DEF_R_SDRAM_CONFIG=0x09e05757 -CONFIG_ETRAX_DEF_R_SDRAM_TIMING=0x80008002 -CONFIG_ETRAX_DEF_R_PORT_PA_DIR=0x1d -CONFIG_ETRAX_DEF_R_PORT_PA_DATA=0x00 -CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=0x00 -CONFIG_ETRAX_DEF_R_PORT_PB_DIR=0x1e -CONFIG_ETRAX_DEF_R_PORT_PB_DATA=0xf3 +CONFIG_ETRAX_DEF_R_WAITSTATES=95a6 +CONFIG_ETRAX_DEF_R_BUS_CONFIG=104 +# CONFIG_ETRAX_SDRAM is not set +CONFIG_ETRAX_DEF_R_DRAM_CONFIG=1a200040 +CONFIG_ETRAX_DEF_R_DRAM_TIMING=5611 +CONFIG_ETRAX_DEF_R_PORT_PA_DIR=1c +CONFIG_ETRAX_DEF_R_PORT_PA_DATA=00 +CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG=00 +CONFIG_ETRAX_DEF_R_PORT_PB_DIR=00 +CONFIG_ETRAX_DEF_R_PORT_PB_DATA=ff # CONFIG_ETRAX_SOFT_SHUTDOWN is not set # +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NF_CONNTRACK_ENABLED is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# # Drivers for built-in interfaces # CONFIG_ETRAX_ETHERNET=y -# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set -CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y CONFIG_ETRAX_SERIAL=y # CONFIG_ETRAX_SERIAL_FAST_TIMER is not set # CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is not set CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS=5 -CONFIG_ETRAX_SERIAL_PORT0=y -# CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT is not set -CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT=y -# CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN is not set -CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN=y -CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE=y -# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA is not set -# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set -# CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED is not set -CONFIG_ETRAX_SER0_DTR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_RI_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_DSR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_CD_ON_PA_BIT=-1 -CONFIG_ETRAX_SER0_DTR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER0_RI_ON_PB_BIT=-1 -CONFIG_ETRAX_SER0_DSR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER0_CD_ON_PB_BIT=-1 +# CONFIG_ETRAX_SERIAL_PORT0 is not set # CONFIG_ETRAX_SERIAL_PORT1 is not set -CONFIG_ETRAX_SERIAL_PORT2=y -# CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT is not set -CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT=y -# CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN is not set -CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN=y -CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE=y -# CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA is not set -# CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PB is not set -# CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED is not set -CONFIG_ETRAX_SER2_DTR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_RI_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_DSR_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_CD_ON_PA_BIT=-1 -CONFIG_ETRAX_SER2_DTR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER2_RI_ON_PB_BIT=-1 -CONFIG_ETRAX_SER2_DSR_ON_PB_BIT=-1 -CONFIG_ETRAX_SER2_CD_ON_PB_BIT=-1 +# CONFIG_ETRAX_SERIAL_PORT2 is not set # CONFIG_ETRAX_SERIAL_PORT3 is not set -CONFIG_ETRAX_RS485=y -# CONFIG_ETRAX_RS485_ON_PA is not set -# CONFIG_ETRAX_RS485_DISABLE_RECEIVER is not set -CONFIG_ETRAX_IDE=y -CONFIG_ETRAX_IDE_DELAY=15 -CONFIG_ETRAX_IDE_PB7_RESET=y -# CONFIG_ETRAX_IDE_G27_RESET is not set -CONFIG_ETRAX_USB_HOST=y -CONFIG_ETRAX_USB_HOST_PORT1=y -CONFIG_ETRAX_USB_HOST_PORT2=y +# CONFIG_ETRAX_RS485 is not set +# CONFIG_ETRAX_IDE is not set +# CONFIG_ETRAX_USB_HOST is not set CONFIG_ETRAX_AXISFLASHMAP=y CONFIG_ETRAX_PTABLE_SECTOR=65536 # CONFIG_ETRAX_I2C is not set # CONFIG_ETRAX_GPIO is not set -CONFIG_ETRAX_RTC=y -CONFIG_ETRAX_DS1302=y -# CONFIG_ETRAX_PCF8563 is not set -CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT=y -CONFIG_ETRAX_DS1302_RSTBIT=0 -CONFIG_ETRAX_DS1302_SCLBIT=1 -CONFIG_ETRAX_DS1302_SDABIT=0 -CONFIG_ETRAX_DS1302_TRICKLE_CHARGE=0 +# CONFIG_ETRAX_RTC is not set +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y # # Generic Driver Options # CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y -# CONFIG_FW_LOADER is not set - -# -# Memory Technology Devices (MTD) -# +# CONFIG_SYS_HYPERVISOR is not set CONFIG_MTD=y # CONFIG_MTD_DEBUG is not set -CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y # CONFIG_MTD_REDBOOT_PARTS is not set # CONFIG_MTD_CMDLINE_PARTS is not set @@ -196,16 +303,20 @@ CONFIG_MTD_CONCAT=y # User Modules And Translation Layers # CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y CONFIG_MTD_BLOCK=y # CONFIG_FTL is not set # CONFIG_NFTL is not set # CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set # # RAM/ROM/Flash chip drivers # CONFIG_MTD_CFI=y -# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_JEDECPROBE=y CONFIG_MTD_GEN_PROBE=y # CONFIG_MTD_CFI_ADV_OPTIONS is not set CONFIG_MTD_MAP_BANK_WIDTH_1=y @@ -220,20 +331,18 @@ CONFIG_MTD_CFI_I2=y # CONFIG_MTD_CFI_I8 is not set # CONFIG_MTD_CFI_INTELEXT is not set CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_CFI_AMDSTD_RETRY=0 # CONFIG_MTD_CFI_STAA is not set CONFIG_MTD_CFI_UTIL=y CONFIG_MTD_RAM=y # CONFIG_MTD_ROM is not set # CONFIG_MTD_ABSENT is not set -# CONFIG_MTD_SHARP is not set -# CONFIG_MTD_JEDEC is not set # # Mapping drivers for chip access # CONFIG_MTD_COMPLEX_MAPPINGS=y # CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set # # Self-contained MTD device drivers @@ -244,7 +353,6 @@ CONFIG_MTD_MTDRAM=y CONFIG_MTDRAM_TOTAL_SIZE=0 CONFIG_MTDRAM_ERASE_SIZE=64 CONFIG_MTDRAM_ABS_POS=0x0 -# CONFIG_MTD_BLKMTD is not set # CONFIG_MTD_BLOCK2MTD is not set # @@ -253,216 +361,54 @@ CONFIG_MTDRAM_ABS_POS=0x0 # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set - -# -# NAND Flash Device Drivers -# # CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set # -# Parallel port support -# -# CONFIG_PARPORT is not set - -# -# Plug and Play support +# UBI - Unsorted block images # - -# -# Block devices -# -# CONFIG_BLK_DEV_FD is not set +# CONFIG_MTD_UBI is not set +CONFIG_BLK_DEV=y # CONFIG_BLK_DEV_COW_COMMON is not set # CONFIG_BLK_DEV_LOOP is not set -# CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 -# CONFIG_BLK_DEV_INITRD is not set -CONFIG_INITRAMFS_SOURCE="" +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 # CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -# CONFIG_IOSCHED_AS is not set -# CONFIG_IOSCHED_DEADLINE is not set -CONFIG_IOSCHED_CFQ=y # CONFIG_ATA_OVER_ETH is not set - -# -# Multi-device support (RAID and LVM) -# -# CONFIG_MD is not set - -# -# ATA/ATAPI/MFM/RLL support -# -# CONFIG_IDE is not set -# CONFIG_PARIDE is not set - -# -# Please see Documentation/ide.txt for help/info on IDE drives -# -# CONFIG_BLK_DEV_IDE_SATA is not set -# CONFIG_IDEDISK_MULTI_MODE is not set -# CONFIG_BLK_DEV_IDETAPE is not set -# CONFIG_BLK_DEV_IDEFLOPPY is not set -# CONFIG_IDE_TASK_IOCTL is not set - -# -# IDE chipset support/bugfixes -# -# CONFIG_IDE_GENERIC is not set -# CONFIG_IDE_ARM is not set -# CONFIG_IDEDMA_AUTO is not set -# CONFIG_BLK_DEV_HD is not set - -# -# SCSI device support -# -# CONFIG_SCSI is not set -# CONFIG_ISCSI_TCP is not set - -# -# IEEE 1394 (FireWire) support -# - -# -# I2O device support -# - -# -# Networking support -# -CONFIG_NET=y - -# -# Networking options -# -CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set -# CONFIG_NETLINK_DEV is not set -CONFIG_UNIX=y -# CONFIG_NET_KEY is not set -CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set -# CONFIG_IP_ADVANCED_ROUTER is not set -# CONFIG_IP_PNP is not set -# CONFIG_NET_IPIP is not set -# CONFIG_NET_IPGRE is not set -# CONFIG_ARPD is not set -# CONFIG_SYN_COOKIES is not set -# CONFIG_INET_AH is not set -# CONFIG_INET_ESP is not set -# CONFIG_INET_IPCOMP is not set -# CONFIG_INET_TUNNEL is not set -CONFIG_IP_TCPDIAG=y -# CONFIG_IP_TCPDIAG_IPV6 is not set - -# -# IP: Virtual Server Configuration -# -# CONFIG_IP_VS is not set -# CONFIG_IPV6 is not set -CONFIG_NETFILTER=y -# CONFIG_NETFILTER_DEBUG is not set - -# -# IP: Netfilter Configuration -# -# CONFIG_IP_NF_CONNTRACK is not set -# CONFIG_IP_NF_CONNTRACK_MARK is not set -# CONFIG_IP_NF_QUEUE is not set -# CONFIG_IP_NF_IPTABLES is not set -# CONFIG_IP_NF_ARPTABLES is not set - -# -# SCTP Configuration (EXPERIMENTAL) -# -# CONFIG_IP_SCTP is not set -# CONFIG_ATM is not set -# CONFIG_BRIDGE is not set -# CONFIG_VLAN_8021Q is not set -# CONFIG_DECNET is not set -# CONFIG_LLC2 is not set -# CONFIG_IPX is not set -# CONFIG_ATALK is not set -# CONFIG_X25 is not set -# CONFIG_LAPB is not set -# CONFIG_NET_DIVERT is not set -# CONFIG_ECONET is not set -# CONFIG_WAN_ROUTER is not set - -# -# QoS and/or fair queueing -# -# CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set - -# -# Network testing -# -# CONFIG_NET_PKTGEN is not set -# CONFIG_NETPOLL is not set -# CONFIG_NET_POLL_CONTROLLER is not set -# CONFIG_HAMRADIO is not set -# CONFIG_IRDA is not set -# CONFIG_AF_RXRPC is not set -# CONFIG_AF_RXRPC_DEBUG is not set -# CONFIG_BT is not set -# CONFIG_I2C is not set - CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set # CONFIG_DUMMY is not set # CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set # CONFIG_EQUALIZER is not set # CONFIG_TUN is not set - -# -# Ethernet (10 or 100Mbit) -# +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set CONFIG_NET_ETHERNET=y -# CONFIG_MII is not set +CONFIG_MII=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y # -# Ethernet (1000 Mbit) -# - -# -# Ethernet (10000 Mbit) -# - -# -# Token Ring devices -# - -# -# Wireless LAN (non-hamradio) -# -# CONFIG_NET_RADIO is not set - -# -# Wan interfaces +# Wireless LAN # +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set # CONFIG_WAN is not set # CONFIG_PPP is not set # CONFIG_SLIP is not set # CONFIG_SHAPER is not set # CONFIG_NETCONSOLE is not set - -# -# ISDN subsystem -# -# CONFIG_ISDN is not set - -# -# Telephony Support -# -# CONFIG_PHONE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_RTC_CLASS is not set # # Input device support @@ -470,7 +416,7 @@ CONFIG_NET_ETHERNET=y # CONFIG_INPUT is not set # -# Input I/O drivers +# Hardware I/O ports # CONFIG_SERIO=y # CONFIG_SERIO_I8042 is not set @@ -480,94 +426,39 @@ CONFIG_SERIO=y # CONFIG_GAMEPORT is not set # -# Input Device Drivers -# -CONFIG_INPUT_KEYBOARD=y -CONFIG_KEYBOARD_ATKBD=y -# CONFIG_KEYBOARD_SUNKBD is not set -# CONFIG_KEYBOARD_LKKBD is not set -# CONFIG_KEYBOARD_XTKBD is not set -# CONFIG_KEYBOARD_NEWTON is not set -CONFIG_INPUT_MOUSE=y -CONFIG_MOUSE_PS2=y -# CONFIG_MOUSE_SERIAL is not set -# CONFIG_MOUSE_VSXXXAA is not set -# CONFIG_INPUT_JOYSTICK is not set -# CONFIG_INPUT_TABLET is not set -# CONFIG_INPUT_TOUCHSCREEN is not set -# CONFIG_INPUT_MISC is not set - -# # Character devices # # CONFIG_VT is not set -# CONFIG_SERIAL_NONSTANDARD is not set - -# -# Serial drivers -# -# CONFIG_SERIAL_8250 is not set - -# -# Non-8250 serial port support -# -CONFIG_SERIAL_CORE=y -CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 - -# -# IPMI -# -# CONFIG_IPMI_HANDLER is not set - -# -# Watchdog Cards -# -# CONFIG_WATCHDOG is not set +CONFIG_HW_RANDOM=y # CONFIG_RTC is not set # CONFIG_GEN_RTC is not set -# CONFIG_DTLK is not set # CONFIG_R3964 is not set -# CONFIG_RTC_LIB is not set -# CONFIG_RTC_CLASS is not set - -# -# Ftape, the floppy tape device driver -# -# CONFIG_DRM is not set # CONFIG_RAW_DRIVER is not set # -# Multimedia devices -# -# CONFIG_VIDEO_DEV is not set - -# -# Digital Video Broadcasting Devices -# -# CONFIG_DVB is not set - -# # File systems # # CONFIG_EXT2_FS is not set # CONFIG_EXT3_FS is not set -# CONFIG_JBD is not set +# CONFIG_EXT4DEV_FS is not set # CONFIG_REISERFS_FS is not set # CONFIG_JFS_FS is not set - -# -# XFS support -# +# CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y # CONFIG_QUOTA is not set CONFIG_DNOTIFY=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set # # CD-ROM/DVD Filesystems @@ -587,13 +478,12 @@ CONFIG_DNOTIFY=y # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y CONFIG_SYSFS=y -# CONFIG_DEVFS_FS is not set -# CONFIG_DEVPTS_FS_XATTR is not set CONFIG_TMPFS=y -# CONFIG_TMPFS_XATTR is not set +# CONFIG_TMPFS_POSIX_ACL is not set # CONFIG_HUGETLB_PAGE is not set -CONFIG_RAMFS=y +# CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems @@ -605,15 +495,15 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set -CONFIG_JFFS_FS=y -CONFIG_JFFS_FS_VERBOSE=0 -# CONFIG_JFFS_PROC_FS is not set CONFIG_JFFS2_FS=y CONFIG_JFFS2_FS_DEBUG=0 -# CONFIG_JFFS2_FS_NAND is not set -# CONFIG_JFFS2_FS_NOR_ECC is not set +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set # CONFIG_JFFS2_COMPRESSION_OPTIONS is not set CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set CONFIG_JFFS2_RTIME=y # CONFIG_JFFS2_RUBIN is not set CONFIG_CRAMFS=y @@ -622,12 +512,10 @@ CONFIG_CRAMFS=y # CONFIG_QNX4FS_FS is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set - -# -# Network File Systems -# +CONFIG_NETWORK_FILESYSTEMS=y CONFIG_NFS_FS=y CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set # CONFIG_NFS_V4 is not set # CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set @@ -649,181 +537,44 @@ CONFIG_SUNRPC=y # # CONFIG_PARTITION_ADVANCED is not set CONFIG_MSDOS_PARTITION=y - -# -# Native Language Support -# # CONFIG_NLS is not set - -# -# Sound -# -# CONFIG_SOUND is not set - -# -# Generic devices -# -# CONFIG_SND_MPU401_UART is not set -# CONFIG_SND_DUMMY is not set -# CONFIG_SND_VIRMIDI is not set -# CONFIG_SND_MTPAV is not set -# CONFIG_SND_SERIAL_U16550 is not set -# CONFIG_SND_MPU401 is not set - -# -# PCCARD (PCMCIA/CardBus) support -# -# CONFIG_PCCARD is not set -# CONFIG_PARPORT_PC_PCMCIA is not set -# CONFIG_NET_PCMCIA is not set - -# -# PC-card bridges -# - -# -# USB support -# -CONFIG_USB=y -# CONFIG_USB_DEBUG is not set - -# -# Miscellaneous USB options -# -CONFIG_USB_DEVICEFS=y -# CONFIG_USB_BANDWIDTH is not set -# CONFIG_USB_DYNAMIC_MINORS is not set -# CONFIG_USB_OTG is not set -# CONFIG_USB_ARCH_HAS_HCD is not set -# CONFIG_USB_ARCH_HAS_OHCI is not set - -# -# USB Host Controller Drivers -# -# CONFIG_USB_SL811_HCD is not set - -# -# USB Device Class drivers -# - -# -# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem -# -# CONFIG_USB_ACM is not set -# CONFIG_USB_PRINTER is not set - -# -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information -# -# CONFIG_USB_STORAGE is not set - -# -# USB Input Devices -# -# CONFIG_USB_HID is not set -# HID_SUPPORT is not set - -# -# USB HID Boot Protocol drivers -# -# CONFIG_USB_KBD is not set -# CONFIG_USB_MOUSE is not set -# CONFIG_USB_AIPTEK is not set -# CONFIG_USB_WACOM is not set -# CONFIG_USB_KBTAB is not set -# CONFIG_USB_POWERMATE is not set -# CONFIG_USB_MTOUCH is not set -# CONFIG_USB_EGALAX is not set -# CONFIG_USB_XPAD is not set -# CONFIG_USB_ATI_REMOTE is not set - -# -# USB Imaging devices -# -# CONFIG_USB_MDC800 is not set - -# -# USB Multimedia devices -# -# CONFIG_USB_DABUSB is not set - -# -# Video4Linux support is needed for USB Multimedia device support -# - -# -# USB Network Adapters -# -# CONFIG_USB_CATC is not set -# CONFIG_USB_KAWETH is not set -# CONFIG_USB_PEGASUS is not set -CONFIG_USB_RTL8150=y -# CONFIG_USB_USBNET is not set - -# -# USB port drivers -# - -# -# USB Serial Converter support -# -# CONFIG_USB_SERIAL is not set - -# -# USB Miscellaneous drivers -# -# CONFIG_USB_EMI62 is not set -# CONFIG_USB_EMI26 is not set -# CONFIG_USB_AUERSWALD is not set -# CONFIG_USB_RIO500 is not set -# CONFIG_USB_LEGOTOWER is not set -# CONFIG_USB_LCD is not set -# CONFIG_USB_LED is not set -# CONFIG_USB_CYTHERM is not set -# CONFIG_USB_PHIDGETKIT is not set -# CONFIG_USB_PHIDGETSERVO is not set -# CONFIG_USB_IDMOUSE is not set -# CONFIG_USB_TEST is not set - -# -# USB ATM/DSL drivers -# - -# -# USB Gadget Support -# -# CONFIG_USB_GADGET is not set +# CONFIG_DLM is not set # # Kernel hacking # # CONFIG_PROFILING is not set # CONFIG_SYSTEM_PROFILER is not set -# CONFIG_ETRAX_KGDB is not set -# CONFIG_DEBUG_INFO is not set -# CONFIG_FRAME_POINTER is not set -# CONFIG_DEBUG_NMI_OOPS is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SAMPLES is not set # # Security options # # CONFIG_KEYS is not set # CONFIG_SECURITY is not set - -# -# Cryptographic options -# +# CONFIG_SECURITY_FILE_CAPABILITIES is not set # CONFIG_CRYPTO is not set # -# Hardware crypto devices -# CONFIG_CRYPTO_HW is not set - -# # Library routines # +CONFIG_BITREVERSE=y # CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set CONFIG_CRC32=y +# CONFIG_CRC7 is not set # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_DMA=y diff --git a/arch/cris/etraxfs_defconfig b/arch/cris/etraxfs_defconfig new file mode 100644 index 00000000000..73c646a3725 --- /dev/null +++ b/arch/cris/etraxfs_defconfig @@ -0,0 +1,585 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24-rc3 +# Fri Nov 30 14:24:26 2007 +# +CONFIG_MMU=y +CONFIG_ZONE_DMA=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NO_IOPORT=y +CONFIG_FORCE_MAX_ZONEORDER=6 +CONFIG_CRIS=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" + +# +# General setup +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_ETRAX_CMDLINE="root=/dev/mtdblock3 init=/linuxrc" +# CONFIG_ETRAX_WATCHDOG is not set +CONFIG_ETRAX_FAST_TIMER=y +# CONFIG_ETRAX_KMALLOCED_MODULES is not set +# CONFIG_OOM_REBOOT is not set +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y + +# +# Hardware setup +# +# CONFIG_ETRAX100LX is not set +# CONFIG_ETRAX100LX_V2 is not set +# CONFIG_SVINTO_SIM is not set +CONFIG_ETRAXFS=y +# CONFIG_CRIS_MACH_ARTPEC3 is not set +# CONFIG_ETRAX_VCS_SIM is not set +# CONFIG_ETRAX_ARCH_V10 is not set +CONFIG_ETRAX_ARCH_V32=y +CONFIG_ETRAX_DRAM_SIZE=32 +CONFIG_ETRAX_FLASH_BUSWIDTH=2 +CONFIG_ETRAX_NANDFLASH_BUSWIDTH=1 +CONFIG_ETRAX_FLASH1_SIZE=4 +CONFIG_ETRAX_DEBUG_PORT0=y +# CONFIG_ETRAX_DEBUG_PORT1 is not set +# CONFIG_ETRAX_DEBUG_PORT2 is not set +# CONFIG_ETRAX_DEBUG_PORT3 is not set +# CONFIG_ETRAX_DEBUG_PORT_NULL is not set +CONFIG_ETRAX_DRAM_VIRTUAL_BASE=c0000000 + +# +# ETRAX FS options +# +CONFIG_ETRAX_SERIAL_PORTS=4 +CONFIG_ETRAX_MEM_GRP1_CONFIG=4044a +CONFIG_ETRAX_MEM_GRP2_CONFIG=0 +CONFIG_ETRAX_MEM_GRP3_CONFIG=0 +CONFIG_ETRAX_MEM_GRP4_CONFIG=0 +CONFIG_ETRAX_SDRAM_GRP0_CONFIG=336 +CONFIG_ETRAX_SDRAM_GRP1_CONFIG=0 +CONFIG_ETRAX_SDRAM_TIMING=104a +CONFIG_ETRAX_SDRAM_COMMAND=0 +CONFIG_ETRAX_DEF_GIO_PA_OE=1c +CONFIG_ETRAX_DEF_GIO_PA_OUT=00 +CONFIG_ETRAX_DEF_GIO_PB_OE=00000 +CONFIG_ETRAX_DEF_GIO_PB_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PC_OE=00000 +CONFIG_ETRAX_DEF_GIO_PC_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PD_OE=00000 +CONFIG_ETRAX_DEF_GIO_PD_OUT=00000 +CONFIG_ETRAX_DEF_GIO_PE_OE=00000 +CONFIG_ETRAX_DEF_GIO_PE_OUT=00000 +# CONFIG_CPU_FREQ is not set +# CONFIG_ETRAX_NBR_LED_GRP_ZERO is not set +CONFIG_ETRAX_NBR_LED_GRP_ONE=y +# CONFIG_ETRAX_NBR_LED_GRP_TWO is not set +CONFIG_ETRAX_LED_G_NET0="PA3" +CONFIG_ETRAX_LED_R_NET0="PA4" +CONFIG_ETRAX_V32_LED2G="PA5" +CONFIG_ETRAX_V32_LED2R="PA6" +CONFIG_ETRAX_V32_LED3G="PA7" +CONFIG_ETRAX_V32_LED3R="PA7" + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NF_CONNTRACK_ENABLED is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Drivers for built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +# CONFIG_ETRAX_IDE is not set +CONFIG_ETRAX_AXISFLASHMAP=y +CONFIG_ETRAX_PTABLE_SECTOR=65536 +# CONFIG_ETRAX_I2C is not set +# CONFIG_ETRAX_GPIO is not set +# CONFIG_ETRAX_NO_PHY is not set +# CONFIG_ETRAX_ETHERNET_IFACE0 is not set +# CONFIG_ETRAX_ETHERNET_IFACE1 is not set +# CONFIG_ETRAXFS_SERIAL is not set +# CONFIG_ETRAX_SYNCHRONOUS_SERIAL is not set +# CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE is not set +# CONFIG_ETRAX_NANDFLASH is not set +# CONFIG_ETRAX_CARDBUS is not set +# CONFIG_ETRAX_IOP_FW_LOAD is not set +# CONFIG_ETRAX_STREAMCOPROC is not set +# CONFIG_ETRAX_SPI_MMC is not set +# CONFIG_ETRAX_SPI_MMC_BOARD is not set +# CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK is not set +CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY=y + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +CONFIG_MTD_RAM=y +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +CONFIG_MTD_MTDRAM=y +CONFIG_MTDRAM_TOTAL_SIZE=0 +CONFIG_MTDRAM_ERASE_SIZE=64 +CONFIG_MTDRAM_ABS_POS=0x0 +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_RTC_CLASS is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +# CONFIG_SERIO_SERPORT is not set +# CONFIG_SERIO_LIBPS2 is not set +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +CONFIG_HW_RANDOM=y +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_CRAMFS=y +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PROFILING is not set +# CONFIG_SYSTEM_PROFILER is not set +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SAMPLES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_DMA=y diff --git a/arch/cris/kernel/module.c b/arch/cris/kernel/module.c index 11b867df861..a187833febc 100644 --- a/arch/cris/kernel/module.c +++ b/arch/cris/kernel/module.c @@ -28,20 +28,28 @@ #define DEBUGP(fmt , ...) #endif +#ifdef CONFIG_ETRAX_KMALLOCED_MODULES +#define MALLOC_MODULE(size) kmalloc(size, GFP_KERNEL) +#define FREE_MODULE(region) kfree(region) +#else +#define MALLOC_MODULE(size) vmalloc_exec(size) +#define FREE_MODULE(region) vfree(region) +#endif + void *module_alloc(unsigned long size) { if (size == 0) return NULL; - return vmalloc_exec(size); + return MALLOC_MODULE(size); } /* Free memory returned from module_alloc */ void module_free(struct module *mod, void *module_region) { - vfree(module_region); + FREE_MODULE(module_region); /* FIXME: If module_region == mod->init_region, trim exception - table entries. */ + table entries. */ } /* We don't need anything special. */ diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c index 9ca558fc5bc..ef2db8fd102 100644 --- a/arch/cris/kernel/process.c +++ b/arch/cris/kernel/process.c @@ -1,5 +1,4 @@ -/* $Id: process.c,v 1.21 2005/03/04 08:16:17 starvik Exp $ - * +/* * linux/arch/cris/kernel/process.c * * Copyright (C) 1995 Linus Torvalds @@ -7,105 +6,6 @@ * * Authors: Bjorn Wesen (bjornw@axis.com) * - * $Log: process.c,v $ - * Revision 1.21 2005/03/04 08:16:17 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.20 2005/01/18 05:57:22 starvik - * Renamed hlt_counter to cris_hlt_counter and made it global. - * - * Revision 1.19 2004/10/19 13:07:43 starvik - * Merge of Linux 2.6.9 - * - * Revision 1.18 2004/08/16 12:37:23 starvik - * Merge of Linux 2.6.8 - * - * Revision 1.17 2004/04/05 13:53:48 starvik - * Merge of Linux 2.6.5 - * - * Revision 1.16 2003/10/27 08:04:33 starvik - * Merge of Linux 2.6.0-test9 - * - * Revision 1.15 2003/09/11 07:29:52 starvik - * Merge of Linux 2.6.0-test5 - * - * Revision 1.14 2003/06/10 10:21:12 johana - * Moved thread_saved_pc() from arch/cris/kernel/process.c to - * subarch specific process.c. arch-v32 has an erp, no irp. - * - * Revision 1.13 2003/04/09 05:20:47 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.12 2002/12/11 15:41:11 starvik - * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/kernel - * - * Revision 1.11 2002/12/10 09:00:10 starvik - * Merge of Linux 2.5.51 - * - * Revision 1.10 2002/11/27 08:42:34 starvik - * Argument to user_regs() is thread_info* - * - * Revision 1.9 2002/11/26 09:44:21 starvik - * New threads exits through ret_from_fork (necessary for preemptive scheduling) - * - * Revision 1.8 2002/11/19 14:35:24 starvik - * Changes from linux 2.4 - * Changed struct initializer syntax to the currently prefered notation - * - * Revision 1.7 2002/11/18 07:39:42 starvik - * thread_saved_pc moved here from processor.h - * - * Revision 1.6 2002/11/14 06:51:27 starvik - * Made cpu_idle more similar with other archs - * init_task_union -> init_thread_union - * Updated for new interrupt macros - * sys_clone and do_fork have a new argument, user_tid - * - * Revision 1.5 2002/11/05 06:45:11 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.4 2002/02/05 15:37:44 bjornw - * Need init_task.h - * - * Revision 1.3 2002/01/21 15:22:49 bjornw - * current->counter is gone - * - * Revision 1.22 2001/11/13 09:40:43 orjanf - * Added dump_fpu (needed for core dumps). - * - * Revision 1.21 2001/11/12 18:26:21 pkj - * Fixed compiler warnings. - * - * Revision 1.20 2001/10/03 08:21:39 jonashg - * cause_of_death does not exist if CONFIG_SVINTO_SIM is defined. - * - * Revision 1.19 2001/09/26 11:52:54 bjornw - * INIT_MMAP is gone in 2.4.10 - * - * Revision 1.18 2001/08/21 21:43:51 hp - * Move last watchdog fix inside #ifdef CONFIG_ETRAX_WATCHDOG - * - * Revision 1.17 2001/08/21 13:48:01 jonashg - * Added fix by HP to avoid oops when doing a hard_reset_now. - * - * Revision 1.16 2001/06/21 02:00:40 hp - * * entry.S: Include asm/unistd.h. - * (_sys_call_table): Use section .rodata, not .data. - * (_kernel_thread): Move from... - * * process.c: ... here. - * * entryoffsets.c (VAL): Break out from... - * (OF): Use VAL. - * (LCLONE_VM): New asmified value from CLONE_VM. - * - * Revision 1.15 2001/06/20 16:31:57 hp - * Add comments to describe empty functions according to review. - * - * Revision 1.14 2001/05/29 11:27:59 markusl - * Fixed so that hard_reset_now will do reset even if watchdog wasn't enabled - * - * Revision 1.13 2001/03/20 19:44:06 bjornw - * Use the 7th syscall argument for regs instead of current_regs - * */ /* @@ -206,6 +106,7 @@ EXPORT_SYMBOL(pm_power_off); * low exit latency (ie sit in a loop waiting for * somebody to say that they'd like to reschedule) */ + void cpu_idle (void) { /* endless idle loop with no priority at all */ diff --git a/arch/cris/kernel/ptrace.c b/arch/cris/kernel/ptrace.c index 3ccd20e85dc..b326023baab 100644 --- a/arch/cris/kernel/ptrace.c +++ b/arch/cris/kernel/ptrace.c @@ -2,65 +2,11 @@ * linux/arch/cris/kernel/ptrace.c * * Parts taken from the m68k port. - * + * * Copyright (c) 2000, 2001, 2002 Axis Communications AB * * Authors: Bjorn Wesen * - * $Log: ptrace.c,v $ - * Revision 1.10 2004/09/22 11:50:01 orjanf - * * Moved get_reg/put_reg to arch-specific files. - * * Added functions to access debug registers (CRISv32). - * * Added support for PTRACE_SINGLESTEP (CRISv32). - * * Added S flag to CCS_MASK (CRISv32). - * - * Revision 1.9 2003/07/04 12:56:11 tobiasa - * Moved arch-specific code to arch-specific files. - * - * Revision 1.8 2003/04/09 05:20:47 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.7 2002/11/27 08:42:34 starvik - * Argument to user_regs() is thread_info* - * - * Revision 1.6 2002/11/20 11:56:11 starvik - * Merge of Linux 2.5.48 - * - * Revision 1.5 2002/11/18 07:41:19 starvik - * Removed warning - * - * Revision 1.4 2002/11/11 12:47:28 starvik - * SYSCALL_TRACE has been moved to thread flags - * - * Revision 1.3 2002/02/05 15:37:18 bjornw - * * Add do_notify_resume (replaces do_signal in the callchain) - * * syscall_trace is now do_syscall_trace - * * current->ptrace flag PT_TRACESYS -> PT_SYSCALLTRACE - * * Keep track of the current->work.syscall_trace counter - * - * Revision 1.2 2001/12/18 13:35:20 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.8 2001/11/12 18:26:21 pkj - * Fixed compiler warnings. - * - * Revision 1.7 2001/09/26 11:53:49 bjornw - * PTRACE_DETACH works more simple in 2.4.10 - * - * Revision 1.6 2001/07/25 16:08:47 bjornw - * PTRACE_ATTACH bulk moved into arch-independent code in 2.4.7 - * - * Revision 1.5 2001/03/26 14:24:28 orjanf - * * Changed loop condition. - * * Added comment documenting non-standard ptrace behaviour. - * - * Revision 1.4 2001/03/20 19:44:41 bjornw - * Use the user_regs macro instead of thread.esp0 - * - * Revision 1.3 2000/12/18 23:45:25 bjornw - * Linux/CRIS first version - * - * */ #include <linux/kernel.h> @@ -85,7 +31,7 @@ extern int do_signal(int canrestart, struct pt_regs *regs); void do_notify_resume(int canrestart, struct pt_regs *regs, - __u32 thread_info_flags ) + __u32 thread_info_flags) { /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) diff --git a/arch/cris/kernel/semaphore.c b/arch/cris/kernel/semaphore.c index b884263d3cd..f137a439041 100644 --- a/arch/cris/kernel/semaphore.c +++ b/arch/cris/kernel/semaphore.c @@ -4,7 +4,6 @@ */ #include <linux/sched.h> -#include <linux/init.h> #include <asm/semaphore-helper.h> /* diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c index 65466c49d7a..04d48dd91dd 100644 --- a/arch/cris/kernel/setup.c +++ b/arch/cris/kernel/setup.c @@ -18,7 +18,7 @@ #include <linux/screen_info.h> #include <linux/utsname.h> #include <linux/pfn.h> - +#include <linux/cpu.h> #include <asm/setup.h> /* @@ -36,6 +36,8 @@ extern unsigned long dram_start, dram_end; extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */ +static struct cpu cpu_devices[NR_CPUS]; + extern void show_etrax_copyright(void); /* arch-vX/kernel/setup.c */ /* This mainly sets up the memory area, and can be really confusing. @@ -45,24 +47,23 @@ extern void show_etrax_copyright(void); /* arch-vX/kernel/setup.c */ * given by the macro __pa(). * * In this DRAM, the kernel code and data is loaded, in the beginning. - * It really starts at c0004000 to make room for some special pages - + * It really starts at c0004000 to make room for some special pages - * the start address is text_start. The kernel data ends at _end. After * this the ROM filesystem is appended (if there is any). - * + * * Between this address and dram_end, we have RAM pages usable to the * boot code and the system. * */ -void __init -setup_arch(char **cmdline_p) +void __init setup_arch(char **cmdline_p) { extern void init_etrax_debug(void); unsigned long bootmap_size; unsigned long start_pfn, max_pfn; unsigned long memory_start; - /* register an initial console printing routine for printk's */ + /* register an initial console printing routine for printk's */ init_etrax_debug(); @@ -121,7 +122,7 @@ setup_arch(char **cmdline_p) min_low_pfn = PAGE_OFFSET >> PAGE_SHIFT; bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn, - min_low_pfn, + min_low_pfn, max_low_pfn); /* And free all memory not belonging to the kernel (addr, size) */ @@ -137,7 +138,7 @@ setup_arch(char **cmdline_p) * Arguments are start, size */ - reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size); + reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size, BOOTMEM_DEFAULT); /* paging_init() sets up the MMU and marks all pages as reserved */ @@ -180,11 +181,23 @@ static void c_stop(struct seq_file *m, void *v) extern int show_cpuinfo(struct seq_file *m, void *v); -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, .show = show_cpuinfo, }; +static int __init topology_init(void) +{ + int i; + + for_each_possible_cpu(i) { + return register_cpu(&cpu_devices[i], i); + } + + return 0; +} + +subsys_initcall(topology_init); diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c index 7a2cc7efbcf..ff4c6aa75de 100644 --- a/arch/cris/kernel/time.c +++ b/arch/cris/kernel/time.c @@ -1,5 +1,4 @@ -/* $Id: time.c,v 1.18 2005/03/04 08:16:17 starvik Exp $ - * +/* * linux/arch/cris/kernel/time.c * * Copyright (C) 1991, 1992, 1995 Linus Torvalds @@ -18,7 +17,7 @@ * Linux/CRIS specific code: * * Authors: Bjorn Wesen - * Johan Adolfsson + * Johan Adolfsson * */ @@ -208,10 +207,16 @@ cris_do_profile(struct pt_regs* regs) #endif #ifdef CONFIG_PROFILING - profile_tick(CPU_PROFILING); + profile_tick(CPU_PROFILING); #endif } +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies * (1000000000 / HZ) + + get_ns_in_jiffie(); +} + static int __init init_udelay(void) { diff --git a/arch/cris/kernel/traps.c b/arch/cris/kernel/traps.c index 520d92205fe..541efbf0937 100644 --- a/arch/cris/kernel/traps.c +++ b/arch/cris/kernel/traps.c @@ -1,66 +1,78 @@ -/* $Id: traps.c,v 1.11 2005/01/24 16:03:19 orjanf Exp $ - * +/* * linux/arch/cris/traps.c * - * Here we handle the break vectors not used by the system call - * mechanism, as well as some general stack/register dumping + * Here we handle the break vectors not used by the system call + * mechanism, as well as some general stack/register dumping * things. - * - * Copyright (C) 2000-2002 Axis Communications AB + * + * Copyright (C) 2000-2007 Axis Communications AB * * Authors: Bjorn Wesen - * Hans-Peter Nilsson + * Hans-Peter Nilsson * */ #include <linux/init.h> #include <linux/module.h> + #include <asm/pgtable.h> #include <asm/uaccess.h> +extern void arch_enable_nmi(void); +extern void stop_watchdog(void); +extern void reset_watchdog(void); +extern void show_registers(struct pt_regs *regs); + +#ifdef CONFIG_DEBUG_BUGVERBOSE +extern void handle_BUG(struct pt_regs *regs); +#else +#define handle_BUG(regs) +#endif + static int kstack_depth_to_print = 24; -extern int raw_printk(const char *fmt, ...); +void (*nmi_handler)(struct pt_regs *); -void show_trace(unsigned long * stack) +void +show_trace(unsigned long *stack) { unsigned long addr, module_start, module_end; extern char _stext, _etext; int i; - raw_printk("\nCall Trace: "); + printk("\nCall Trace: "); - i = 1; - module_start = VMALLOC_START; - module_end = VMALLOC_END; + i = 1; + module_start = VMALLOC_START; + module_end = VMALLOC_END; - while (((long) stack & (THREAD_SIZE-1)) != 0) { - if (__get_user (addr, stack)) { + while (((long)stack & (THREAD_SIZE-1)) != 0) { + if (__get_user(addr, stack)) { /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); + printk("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; - /* - * If the address is either in the text segment of the - * kernel, or in the region which contains vmalloc'ed - * memory, it *may* be the address of a calling - * routine; if so, print it so that someone tracing - * down the cause of the crash will be able to figure - * out the call path that was taken. - */ - if (((addr >= (unsigned long) &_stext) && - (addr <= (unsigned long) &_etext)) || - ((addr >= module_start) && (addr <= module_end))) { - if (i && ((i % 8) == 0)) - raw_printk("\n "); - raw_printk("[<%08lx>] ", addr); - i++; - } - } + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long)&_stext) && + (addr <= (unsigned long)&_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } } /* @@ -78,109 +90,149 @@ void show_trace(unsigned long * stack) * with the ksymoops maintainer. */ -void +void show_stack(struct task_struct *task, unsigned long *sp) { - unsigned long *stack, addr; - int i; + unsigned long *stack, addr; + int i; /* * debugging aid: "show_stack(NULL);" prints a * back trace. */ - if(sp == NULL) { + if (sp == NULL) { if (task) sp = (unsigned long*)task->thread.ksp; else sp = (unsigned long*)rdsp(); } - stack = sp; + stack = sp; - raw_printk("\nStack from %08lx:\n ", (unsigned long)stack); - for(i = 0; i < kstack_depth_to_print; i++) { - if (((long) stack & (THREAD_SIZE-1)) == 0) - break; - if (i && ((i % 8) == 0)) - raw_printk("\n "); - if (__get_user (addr, stack)) { + printk("\nStack from %08lx:\n ", (unsigned long)stack); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((long)stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + if (__get_user(addr, stack)) { /* This message matches "failing address" marked s390 in ksymoops, so lines containing it will not be filtered out by ksymoops. */ - raw_printk ("Failing address 0x%lx\n", (unsigned long)stack); + printk("Failing address 0x%lx\n", (unsigned long)stack); break; } stack++; - raw_printk("%08lx ", addr); - } + printk("%08lx ", addr); + } show_trace(sp); } -static void (*nmi_handler)(struct pt_regs*); -extern void arch_enable_nmi(void); +#if 0 +/* displays a short stack trace */ -void set_nmi_handler(void (*handler)(struct pt_regs*)) +int +show_stack(void) { - nmi_handler = handler; - arch_enable_nmi(); + unsigned long *sp = (unsigned long *)rdusp(); + int i; + + printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); + for (i = 0; i < 16; i++) + printk("sp + %d: 0x%08lx\n", i*4, sp[i]); + return 0; } +#endif -void handle_nmi(struct pt_regs* regs) +void +dump_stack(void) { - if (nmi_handler) - nmi_handler(regs); + show_stack(NULL, NULL); +} +EXPORT_SYMBOL(dump_stack); + +void +set_nmi_handler(void (*handler)(struct pt_regs *)) +{ + nmi_handler = handler; + arch_enable_nmi(); } #ifdef CONFIG_DEBUG_NMI_OOPS -void oops_nmi_handler(struct pt_regs* regs) +void +oops_nmi_handler(struct pt_regs *regs) { - stop_watchdog(); - raw_printk("NMI!\n"); - show_registers(regs); + stop_watchdog(); + oops_in_progress = 1; + printk("NMI!\n"); + show_registers(regs); + oops_in_progress = 0; } -static int -__init oops_nmi_register(void) +static int __init +oops_nmi_register(void) { - set_nmi_handler(oops_nmi_handler); - return 0; + set_nmi_handler(oops_nmi_handler); + return 0; } __initcall(oops_nmi_register); #endif -#if 0 -/* displays a short stack trace */ - -int -show_stack() +/* + * This gets called from entry.S when the watchdog has bitten. Show something + * similiar to an Oops dump, and if the kernel is configured to be a nice + * doggy, then halt instead of reboot. + */ +void +watchdog_bite_hook(struct pt_regs *regs) { - unsigned long *sp = (unsigned long *)rdusp(); - int i; - raw_printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); - for(i = 0; i < 16; i++) - raw_printk("sp + %d: 0x%08lx\n", i*4, sp[i]); - return 0; -} +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + local_irq_disable(); + stop_watchdog(); + show_registers(regs); + + while (1) + ; /* Do nothing. */ +#else + show_registers(regs); #endif +} -void dump_stack(void) +/* This is normally the Oops function. */ +void +die_if_kernel(const char *str, struct pt_regs *regs, long err) { - show_stack(NULL, NULL); -} + if (user_mode(regs)) + return; -EXPORT_SYMBOL(dump_stack); +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + /* + * This printout might take too long and could trigger + * the watchdog normally. If NICE_DOGGY is set, simply + * stop the watchdog during the printout. + */ + stop_watchdog(); +#endif -void __init -trap_init(void) -{ - /* Nothing needs to be done */ + handle_BUG(regs); + + printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + + oops_in_progress = 0; + +#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY + reset_watchdog(); +#endif + do_exit(SIGSEGV); } -void spinning_cpu(void* addr) +void __init +trap_init(void) { - raw_printk("CPU %d spinning on %X\n", smp_processor_id(), addr); - dump_stack(); + /* Nothing needs to be done */ } diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c index 3034f3ff950..c4c76db90f9 100644 --- a/arch/cris/mm/fault.c +++ b/arch/cris/mm/fault.c @@ -1,130 +1,9 @@ /* * linux/arch/cris/mm/fault.c * - * Copyright (C) 2000, 2001 Axis Communications AB - * - * Authors: Bjorn Wesen - * - * $Log: fault.c,v $ - * Revision 1.20 2005/03/04 08:16:18 starvik - * Merge of Linux 2.6.11. - * - * Revision 1.19 2005/01/14 10:07:59 starvik - * Fixed warning. - * - * Revision 1.18 2005/01/12 08:10:14 starvik - * Re-added the change of frametype when handling kernel page fault fixup - * for v10. This is necessary to avoid that the CPU remakes the faulting - * access. - * - * Revision 1.17 2005/01/11 13:53:05 starvik - * Use raw_printk. - * - * Revision 1.16 2004/12/17 11:39:41 starvik - * SMP support. - * - * Revision 1.15 2004/11/23 18:36:18 starvik - * Stack is now non-executable. - * Signal handler trampolines are placed in a reserved page mapped into all - * processes. - * - * Revision 1.14 2004/11/23 07:10:21 starvik - * Moved find_fixup_code to generic code. - * - * Revision 1.13 2004/11/23 07:00:54 starvik - * Actually use the execute permission bit in the MMU. This makes it possible - * to prevent e.g. attacks where executable code is put on the stack. - * - * Revision 1.12 2004/09/29 06:16:04 starvik - * Use instruction_pointer - * - * Revision 1.11 2004/05/14 07:58:05 starvik - * Merge of changes from 2.4 - * - * Revision 1.10 2003/10/27 14:51:24 starvik - * Removed debugcode - * - * Revision 1.9 2003/10/27 14:50:42 starvik - * Changed do_page_fault signature - * - * Revision 1.8 2003/07/04 13:02:48 tobiasa - * Moved code snippet from arch/cris/mm/fault.c that searches for fixup code - * to separate function in arch-specific files. - * - * Revision 1.7 2003/01/22 06:48:38 starvik - * Fixed warnings issued by GCC 3.2.1 - * - * Revision 1.6 2003/01/09 14:42:52 starvik - * Merge of Linux 2.5.55 - * - * Revision 1.5 2002/12/11 14:44:48 starvik - * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm - * - * Revision 1.4 2002/11/13 15:10:28 starvik - * pte_offset has been renamed to pte_offset_kernel - * - * Revision 1.3 2002/11/05 06:45:13 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.2 2001/12/18 13:35:22 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.20 2001/11/22 13:34:06 bjornw - * * Bug workaround (LX TR89): force a rerun of the whole of an interrupted - * unaligned write, because the second half of the write will be corrupted - * otherwise. Affected unaligned writes spanning not-yet mapped pages. - * * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss - * was due to a read or a write (before we didn't know this until the next - * restart of the interrupted instruction, thus wasting one fault-irq) - * - * Revision 1.19 2001/11/12 19:02:10 pkj - * Fixed compiler warnings. - * - * Revision 1.18 2001/07/18 22:14:32 bjornw - * Enable interrupts in the bulk of do_page_fault - * - * Revision 1.17 2001/07/18 13:07:23 bjornw - * * Detect non-existant PTE's in vmalloc pmd synchronization - * * Remove comment about fast-paths for VMALLOC_START etc, because all that - * was totally bogus anyway it turned out :) - * * Fix detection of vmalloc-area synchronization - * * Add some comments - * - * Revision 1.16 2001/06/13 00:06:08 bjornw - * current_pgd should be volatile - * - * Revision 1.15 2001/06/13 00:02:23 bjornw - * Use a separate variable to store the current pgd to avoid races in schedule - * - * Revision 1.14 2001/05/16 17:41:07 hp - * Last comment tweak further tweaked. - * - * Revision 1.13 2001/05/15 00:58:44 hp - * Expand a bit on the comment why we compare address >= TASK_SIZE rather - * than >= VMALLOC_START. - * - * Revision 1.12 2001/04/04 10:51:14 bjornw - * mmap_sem is grabbed for reading - * - * Revision 1.11 2001/03/23 07:36:07 starvik - * Corrected according to review remarks - * - * Revision 1.10 2001/03/21 16:10:11 bjornw - * CRIS_FRAME_FIXUP not needed anymore, use FRAME_NORMAL - * - * Revision 1.9 2001/03/05 13:22:20 bjornw - * Spell-fix and fix in vmalloc_fault handling - * - * Revision 1.8 2000/11/22 14:45:31 bjornw - * * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping - * into all processes. Instead we fill in the missing PTE entries on demand. - * - * Revision 1.7 2000/11/21 16:39:09 bjornw - * fixup switches frametype - * - * Revision 1.6 2000/11/17 16:54:08 bjornw - * More detailed siginfo reporting + * Copyright (C) 2000-2006 Axis Communications AB * + * Authors: Bjorn Wesen * */ @@ -135,7 +14,6 @@ extern int find_fixup_code(struct pt_regs *); extern void die_if_kernel(const char *, struct pt_regs *, long); -extern int raw_printk(const char *fmt, ...); /* debug of low-level TLB reload */ #undef DEBUG @@ -164,8 +42,8 @@ unsigned long cris_signal_return_page; * address. * * error_code: - * bit 0 == 0 means no page found, 1 means protection fault - * bit 1 == 0 means read, 1 means write + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write * * If this routine detects a bad access, it returns 1, otherwise it * returns 0. @@ -181,9 +59,10 @@ do_page_fault(unsigned long address, struct pt_regs *regs, siginfo_t info; int fault; - D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n", - address, smp_processor_id(), instruction_pointer(regs), - protection, writeaccess)); + D(printk(KERN_DEBUG + "Page fault for %lX on %X at %lX, prot %d write %d\n", + address, smp_processor_id(), instruction_pointer(regs), + protection, writeaccess)); tsk = current; @@ -233,7 +112,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * context, we must not take the fault.. */ - if (in_atomic() || !mm) + if (in_interrupt() || !mm) goto no_context; down_read(&mm->mmap_sem); @@ -319,6 +198,9 @@ do_page_fault(unsigned long address, struct pt_regs *regs, /* info.si_code has been set above */ info.si_addr = (void *)address; force_sig_info(SIGSEGV, &info, tsk); + printk(KERN_NOTICE "%s (pid %d) segfaults for page " + "address %08lx at pc %08lx\n", + tsk->comm, tsk->pid, address, instruction_pointer(regs)); return; } @@ -326,7 +208,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, /* Are we prepared to handle this kernel fault? * - * (The kernel has valid exception-points in the source + * (The kernel has valid exception-points in the source * when it acesses user-memory. When it fails in one * of those points, we find it in a table and do a jump * to some fixup code that loads an appropriate error @@ -341,13 +223,18 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * terminate things with extreme prejudice. */ - if ((unsigned long) (address) < PAGE_SIZE) - raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); - else - raw_printk(KERN_ALERT "Unable to handle kernel access"); - raw_printk(" at virtual address %08lx\n",address); - - die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + if (!oops_in_progress) { + oops_in_progress = 1; + if ((unsigned long) (address) < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL " + "pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel access" + " at virtual address %08lx\n", address); + + die_if_kernel("Oops", regs, (writeaccess << 1) | protection); + oops_in_progress = 0; + } do_exit(SIGKILL); @@ -360,7 +247,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, up_read(&mm->mmap_sem); printk("VM: killing process %s\n", tsk->comm); if (user_mode(regs)) - do_group_exit(SIGKILL); + do_exit(SIGKILL); goto no_context; do_sigbus: @@ -406,8 +293,8 @@ vmalloc_fault: /* Since we're two-level, we don't need to do both * set_pgd and set_pmd (they do the same thing). If * we go three-level at some point, do the right thing - * with pgd_present and set_pgd here. - * + * with pgd_present and set_pgd here. + * * Also, since the vmalloc area is global, we don't * need to copy individual PTE's, it is enough to * copy the pgd pointer into the pte page of the diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c index 0c833d17622..4207a2b5275 100644 --- a/arch/cris/mm/init.c +++ b/arch/cris/mm/init.c @@ -6,117 +6,6 @@ * * Authors: Bjorn Wesen (bjornw@axis.com) * - * $Log: init.c,v $ - * Revision 1.11 2004/05/28 09:28:56 starvik - * Calculation of loops_per_usec moved because initialization order has changed - * in Linux 2.6. - * - * Revision 1.10 2004/05/14 07:58:05 starvik - * Merge of changes from 2.4 - * - * Revision 1.9 2003/07/04 08:27:54 starvik - * Merge of Linux 2.5.74 - * - * Revision 1.8 2003/04/09 05:20:48 starvik - * Merge of Linux 2.5.67 - * - * Revision 1.7 2003/01/22 06:48:38 starvik - * Fixed warnings issued by GCC 3.2.1 - * - * Revision 1.6 2002/12/11 14:44:48 starvik - * Extracted v10 (ETRAX 100LX) specific stuff to arch/cris/arch-v10/mm - * - * Revision 1.5 2002/11/18 07:37:37 starvik - * Added cache bug workaround (from Linux 2.4) - * - * Revision 1.4 2002/11/13 15:40:24 starvik - * Removed the page table cache stuff (as done in other archs) - * - * Revision 1.3 2002/11/05 06:45:13 starvik - * Merge of Linux 2.5.45 - * - * Revision 1.2 2001/12/18 13:35:22 bjornw - * Applied the 2.4.13->2.4.16 CRIS patch to 2.5.1 (is a copy of 2.4.15). - * - * Revision 1.31 2001/11/13 16:22:00 bjornw - * Skip calculating totalram and sharedram in si_meminfo - * - * Revision 1.30 2001/11/12 19:02:10 pkj - * Fixed compiler warnings. - * - * Revision 1.29 2001/07/25 16:09:50 bjornw - * val->sharedram will stay 0 - * - * Revision 1.28 2001/06/28 16:30:17 bjornw - * Oops. This needs to wait until 2.4.6 is merged - * - * Revision 1.27 2001/06/28 14:04:07 bjornw - * Fill in sharedram - * - * Revision 1.26 2001/06/18 06:36:02 hp - * Enable free_initmem of __init-type pages - * - * Revision 1.25 2001/06/13 00:02:23 bjornw - * Use a separate variable to store the current pgd to avoid races in schedule - * - * Revision 1.24 2001/05/15 00:52:20 hp - * Only map segment 0xa as seg if CONFIG_JULIETTE - * - * Revision 1.23 2001/04/04 14:35:40 bjornw - * * Removed get_pte_slow and friends (2.4.3 change) - * * Removed bad_pmd handling (2.4.3 change) - * - * Revision 1.22 2001/04/04 13:38:04 matsfg - * Moved ioremap to a separate function instead - * - * Revision 1.21 2001/03/27 09:28:33 bjornw - * ioremap used too early - lets try it in mem_init instead - * - * Revision 1.20 2001/03/23 07:39:21 starvik - * Corrected according to review remarks - * - * Revision 1.19 2001/03/15 14:25:17 bjornw - * More general shadow registers and ioremaped addresses for external I/O - * - * Revision 1.18 2001/02/23 12:46:44 bjornw - * * 0xc was not CSE1; 0x8 is, same as uncached flash, so we move the uncached - * flash during CRIS_LOW_MAP from 0xe to 0x8 so both the flash and the I/O - * is mapped straight over (for !CRIS_LOW_MAP the uncached flash is still 0xe) - * - * Revision 1.17 2001/02/22 15:05:21 bjornw - * Map 0x9 straight over during LOW_MAP to allow for memory mapped LEDs - * - * Revision 1.16 2001/02/22 15:02:35 bjornw - * Map 0xc straight over during LOW_MAP to allow for memory mapped I/O - * - * Revision 1.15 2001/01/10 21:12:10 bjornw - * loops_per_sec -> loops_per_jiffy - * - * Revision 1.14 2000/11/22 16:23:20 bjornw - * Initialize totalhigh counters to 0 to make /proc/meminfo look nice. - * - * Revision 1.13 2000/11/21 16:37:51 bjornw - * Temporarily disable initmem freeing - * - * Revision 1.12 2000/11/21 13:55:07 bjornw - * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type - * - * Revision 1.11 2000/10/06 12:38:22 bjornw - * Cast empty_bad_page correctly (should really be of * type from the start.. - * - * Revision 1.10 2000/10/04 16:53:57 bjornw - * Fix memory-map due to LX features - * - * Revision 1.9 2000/09/13 15:47:49 bjornw - * Wrong count in reserved-pages loop - * - * Revision 1.8 2000/09/13 14:35:10 bjornw - * 2.4.0-test8 added a new arg to free_area_init_node - * - * Revision 1.7 2000/08/17 15:35:55 bjornw - * 2.4.0-test6 removed MAP_NR and inserted virt_to_page - * - * */ #include <linux/init.h> diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index 96f7d70f447..9e561ede092 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -57,6 +57,10 @@ config ARCH_HAS_ILOG2_U64 bool default y +config HZ + int + default 1000 + mainmenu "Fujitsu FR-V Kernel Configuration" source "init/Kconfig" diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c index b38ae1fc15f..0669e138238 100644 --- a/arch/frv/kernel/setup.c +++ b/arch/frv/kernel/setup.c @@ -925,13 +925,15 @@ static void __init setup_linux_memory(void) #endif /* take back the memory occupied by the kernel image and the bootmem alloc map */ - reserve_bootmem(kstart, kend - kstart + bootmap_size); + reserve_bootmem(kstart, kend - kstart + bootmap_size, + BOOTMEM_DEFAULT); /* reserve the memory occupied by the initial ramdisk */ #ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (low_top_pfn << PAGE_SHIFT)) { - reserve_bootmem(INITRD_START, INITRD_SIZE); + reserve_bootmem(INITRD_START, INITRD_SIZE, + BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET; initrd_end = initrd_start + INITRD_SIZE; } @@ -986,9 +988,10 @@ static void __init setup_uclinux_memory(void) /* now take back the bits the core kernel is occupying */ #ifndef CONFIG_PROTECT_KERNEL - reserve_bootmem(kend, bootmap_size); + reserve_bootmem(kend, bootmap_size, BOOTMEM_DEFAULT); reserve_bootmem((unsigned long) &__kernel_image_start, - kend - (unsigned long) &__kernel_image_start); + kend - (unsigned long) &__kernel_image_start, + BOOTMEM_DEFAULT); #else dampr = __get_DAMPR(0); @@ -996,14 +999,15 @@ static void __init setup_uclinux_memory(void) dampr = (dampr >> 4) + 17; dampr = 1 << dampr; - reserve_bootmem(__get_DAMPR(0) & xAMPRx_PPFN, dampr); + reserve_bootmem(__get_DAMPR(0) & xAMPRx_PPFN, dampr, BOOTMEM_DEFAULT); #endif /* reserve some memory to do uncached DMA through if requested */ #ifdef CONFIG_RESERVE_DMA_COHERENT if (dma_coherent_mem_start) reserve_bootmem(dma_coherent_mem_start, - dma_coherent_mem_end - dma_coherent_mem_start); + dma_coherent_mem_end - dma_coherent_mem_start, + BOOTMEM_DEFAULT); #endif } /* end setup_uclinux_memory() */ @@ -1109,7 +1113,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/frv/mm/pgalloc.c b/arch/frv/mm/pgalloc.c index 1a2e5c8d03a..66f616fb486 100644 --- a/arch/frv/mm/pgalloc.c +++ b/arch/frv/mm/pgalloc.c @@ -28,7 +28,7 @@ pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *page; @@ -37,9 +37,11 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) #else page = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0); #endif - if (page) + if (page) { clear_highpage(page); - flush_dcache_page(page); + pgtable_page_ctor(page); + flush_dcache_page(page); + } return page; } diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index dc61222e112..f69e5ea3855 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -65,6 +65,9 @@ config TIME_LOW_RES bool default y +config ARCH_SUPPORTS_AOUT + def_bool y + config NO_IOPORT def_bool y @@ -79,6 +82,10 @@ config PCI bool default n +config HZ + int + default 100 + source "init/Kconfig" source "arch/h8300/Kconfig.cpu" diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c index b2e86d0255e..b1f25c20a5d 100644 --- a/arch/h8300/kernel/setup.c +++ b/arch/h8300/kernel/setup.c @@ -173,7 +173,7 @@ void __init setup_arch(char **cmdline_p) * the bootmem bitmap so we then reserve it after freeing it :-) */ free_bootmem(memory_start, memory_end - memory_start); - reserve_bootmem(memory_start, bootmap_size); + reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); /* * get kmalloc into gear */ @@ -236,7 +236,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/ia64/kernel/acpi-processor.c b/arch/ia64/kernel/acpi-processor.c index 5a216c01992..cbe6cee5a55 100644 --- a/arch/ia64/kernel/acpi-processor.c +++ b/arch/ia64/kernel/acpi-processor.c @@ -45,6 +45,12 @@ static void init_intel_pdc(struct acpi_processor *pr) buf[0] = ACPI_PDC_REVISION_ID; buf[1] = 1; buf[2] = ACPI_PDC_EST_CAPABILITY_SMP; + /* + * The default of PDC_SMP_T_SWCOORD bit is set for IA64 cpu so + * that OSPM is capable of native ACPI throttling software + * coordination using BIOS supplied _TSD info. + */ + buf[2] |= ACPI_PDC_SMP_T_SWCOORD; obj->type = ACPI_TYPE_BUFFER; obj->buffer.length = 12; diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 00b5d08f6da..78f28d825f3 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -69,6 +69,20 @@ unsigned int acpi_cpei_phys_cpuid; unsigned long acpi_wakeup_address = 0; +#ifdef CONFIG_IA64_GENERIC +static unsigned long __init acpi_find_rsdp(void) +{ + unsigned long rsdp_phys = 0; + + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) + rsdp_phys = efi.acpi20; + else if (efi.acpi != EFI_INVALID_TABLE_ADDR) + printk(KERN_WARNING PREFIX + "v1.0/r0.71 tables no longer supported\n"); + return rsdp_phys; +} +#endif + const char __init * acpi_get_sysname(void) { @@ -152,7 +166,7 @@ int acpi_request_vector(u32 int_type) return vector; } -char *__acpi_map_table(unsigned long phys_addr, unsigned long size) +char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size) { return __va(phys_addr); } @@ -601,8 +615,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity) IOSAPIC_LEVEL); } -EXPORT_SYMBOL(acpi_register_gsi); - void acpi_unregister_gsi(u32 gsi) { if (acpi_irq_model == ACPI_IRQ_MODEL_PLATFORM) @@ -611,8 +623,6 @@ void acpi_unregister_gsi(u32 gsi) iosapic_unregister_intr(gsi); } -EXPORT_SYMBOL(acpi_unregister_gsi); - static int __init acpi_parse_fadt(struct acpi_table_header *table) { struct acpi_table_header *fadt_header; @@ -631,18 +641,6 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table) return 0; } -unsigned long __init acpi_find_rsdp(void) -{ - unsigned long rsdp_phys = 0; - - if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) - rsdp_phys = efi.acpi20; - else if (efi.acpi != EFI_INVALID_TABLE_ADDR) - printk(KERN_WARNING PREFIX - "v1.0/r0.71 tables no longer supported\n"); - return rsdp_phys; -} - int __init acpi_boot_init(void) { diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index f5d3efbfbed..3c331c464b4 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1573,7 +1573,7 @@ sys_call_table: data8 sys_fchmodat data8 sys_faccessat data8 sys_pselect6 - data8 sys_ppoll + data8 sys_ppoll // 1295 data8 sys_unshare data8 sys_splice data8 sys_set_robust_list @@ -1588,5 +1588,8 @@ sys_call_table: data8 sys_signalfd data8 sys_ni_syscall data8 sys_eventfd + data8 sys_timerfd_create // 1310 + data8 sys_timerfd_settime + data8 sys_timerfd_gettime .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls diff --git a/arch/ia64/kernel/machine_kexec.c b/arch/ia64/kernel/machine_kexec.c index d6cd45f4c6c..0823de1f6eb 100644 --- a/arch/ia64/kernel/machine_kexec.c +++ b/arch/ia64/kernel/machine_kexec.c @@ -129,13 +129,14 @@ void machine_kexec(struct kimage *image) void arch_crash_save_vmcoreinfo(void) { -#if defined(CONFIG_ARCH_DISCONTIGMEM_ENABLE) && defined(CONFIG_NUMA) +#if defined(CONFIG_DISCONTIGMEM) || defined(CONFIG_SPARSEMEM) VMCOREINFO_SYMBOL(pgdat_list); VMCOREINFO_LENGTH(pgdat_list, MAX_NUMNODES); - +#endif +#ifdef CONFIG_NUMA VMCOREINFO_SYMBOL(node_memblk); VMCOREINFO_LENGTH(node_memblk, NR_NODE_MEMBLKS); - VMCOREINFO_SIZE(node_memblk_s); + VMCOREINFO_STRUCT_SIZE(node_memblk_s); VMCOREINFO_OFFSET(node_memblk_s, start_paddr); VMCOREINFO_OFFSET(node_memblk_s, size); #endif diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 846e7e036b1..6e17aed5313 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -17,7 +17,7 @@ * Copyright (C) 2000 Intel * Copyright (C) Chuck Fleckenstein <cfleck@co.intel.com> * - * Copyright (C) 1999, 2004 Silicon Graphics, Inc. + * Copyright (C) 1999, 2004-2008 Silicon Graphics, Inc. * Copyright (C) Vijay Chander <vijay@engr.sgi.com> * * Copyright (C) 2006 FUJITSU LIMITED @@ -1762,11 +1762,8 @@ format_mca_init_stack(void *mca_data, unsigned long offset, /* Caller prevents this from being called after init */ static void * __init_refok mca_bootmem(void) { - void *p; - - p = alloc_bootmem(sizeof(struct ia64_mca_cpu) * NR_CPUS + - KERNEL_STACK_SIZE); - return (void *)ALIGN((unsigned long)p, KERNEL_STACK_SIZE); + return __alloc_bootmem(sizeof(struct ia64_mca_cpu), + KERNEL_STACK_SIZE, 0); } /* Do per-CPU MCA-related initialization. */ @@ -1774,33 +1771,33 @@ void __cpuinit ia64_mca_cpu_init(void *cpu_data) { void *pal_vaddr; + void *data; + long sz = sizeof(struct ia64_mca_cpu); + int cpu = smp_processor_id(); static int first_time = 1; - if (first_time) { - void *mca_data; - int cpu; - - first_time = 0; - mca_data = mca_bootmem(); - for (cpu = 0; cpu < NR_CPUS; cpu++) { - format_mca_init_stack(mca_data, - offsetof(struct ia64_mca_cpu, mca_stack), - "MCA", cpu); - format_mca_init_stack(mca_data, - offsetof(struct ia64_mca_cpu, init_stack), - "INIT", cpu); - __per_cpu_mca[cpu] = __pa(mca_data); - mca_data += sizeof(struct ia64_mca_cpu); - } - } - /* - * The MCA info structure was allocated earlier and its - * physical address saved in __per_cpu_mca[cpu]. Copy that - * address * to ia64_mca_data so we can access it as a per-CPU - * variable. + * Structure will already be allocated if cpu has been online, + * then offlined. */ - __get_cpu_var(ia64_mca_data) = __per_cpu_mca[smp_processor_id()]; + if (__per_cpu_mca[cpu]) { + data = __va(__per_cpu_mca[cpu]); + } else { + if (first_time) { + data = mca_bootmem(); + first_time = 0; + } else + data = page_address(alloc_pages_node(numa_node_id(), + GFP_KERNEL, get_order(sz))); + if (!data) + panic("Could not allocate MCA memory for cpu %d\n", + cpu); + } + format_mca_init_stack(data, offsetof(struct ia64_mca_cpu, mca_stack), + "MCA", cpu); + format_mca_init_stack(data, offsetof(struct ia64_mca_cpu, init_stack), + "INIT", cpu); + __get_cpu_var(ia64_mca_data) = __per_cpu_mca[cpu] = __pa(data); /* * Stash away a copy of the PTE needed to map the per-CPU page. diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c index 78acd9fe97e..f6b99719f10 100644 --- a/arch/ia64/kernel/perfmon.c +++ b/arch/ia64/kernel/perfmon.c @@ -586,21 +586,6 @@ pfm_put_task(struct task_struct *task) } static inline void -pfm_set_task_notify(struct task_struct *task) -{ - struct thread_info *info; - - info = (struct thread_info *) ((char *) task + IA64_TASK_SIZE); - set_bit(TIF_PERFMON_WORK, &info->flags); -} - -static inline void -pfm_clear_task_notify(void) -{ - clear_thread_flag(TIF_PERFMON_WORK); -} - -static inline void pfm_reserve_page(unsigned long a) { SetPageReserved(vmalloc_to_page((void *)a)); @@ -3724,7 +3709,7 @@ pfm_restart(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) PFM_SET_WORK_PENDING(task, 1); - pfm_set_task_notify(task); + tsk_set_notify_resume(task); /* * XXX: send reschedule if task runs on another CPU @@ -5082,7 +5067,7 @@ pfm_handle_work(void) PFM_SET_WORK_PENDING(current, 0); - pfm_clear_task_notify(); + tsk_clear_notify_resume(current); regs = task_pt_regs(current); @@ -5450,7 +5435,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str * when coming from ctxsw, current still points to the * previous task, therefore we must work with task and not current. */ - pfm_set_task_notify(task); + tsk_set_notify_resume(task); } /* * defer until state is changed (shorten spin window). the context is locked diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 7377d323131..49937a383b2 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -52,7 +52,6 @@ #include "sigframe.h" void (*ia64_mark_idle)(int); -static DEFINE_PER_CPU(unsigned int, cpu_idle_state); unsigned long boot_option_idle_override = 0; EXPORT_SYMBOL(boot_option_idle_override); @@ -157,6 +156,17 @@ show_regs (struct pt_regs *regs) show_stack(NULL, NULL); } +void tsk_clear_notify_resume(struct task_struct *tsk) +{ +#ifdef CONFIG_PERFMON + if (tsk->thread.pfm_needs_checking) + return; +#endif + if (test_ti_thread_flag(task_thread_info(tsk), TIF_RESTORE_RSE)) + return; + clear_ti_thread_flag(task_thread_info(tsk), TIF_NOTIFY_RESUME); +} + void do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall) { @@ -175,6 +185,10 @@ do_notify_resume_user (sigset_t *unused, struct sigscratch *scr, long in_syscall /* deal with pending signal delivery */ if (test_thread_flag(TIF_SIGPENDING)||test_thread_flag(TIF_RESTORE_SIGMASK)) ia64_do_signal(scr, in_syscall); + + /* copy user rbs to kernel rbs */ + if (unlikely(test_thread_flag(TIF_RESTORE_RSE))) + ia64_sync_krbs(); } static int pal_halt = 1; @@ -239,33 +253,23 @@ static inline void play_dead(void) } #endif /* CONFIG_HOTPLUG_CPU */ -void cpu_idle_wait(void) +static void do_nothing(void *unused) { - unsigned int cpu, this_cpu = get_cpu(); - cpumask_t map; - cpumask_t tmp = current->cpus_allowed; - - set_cpus_allowed(current, cpumask_of_cpu(this_cpu)); - put_cpu(); - - cpus_clear(map); - for_each_online_cpu(cpu) { - per_cpu(cpu_idle_state, cpu) = 1; - cpu_set(cpu, map); - } - - __get_cpu_var(cpu_idle_state) = 0; +} - wmb(); - do { - ssleep(1); - for_each_online_cpu(cpu) { - if (cpu_isset(cpu, map) && !per_cpu(cpu_idle_state, cpu)) - cpu_clear(cpu, map); - } - cpus_and(map, map, cpu_online_map); - } while (!cpus_empty(map)); - set_cpus_allowed(current, tmp); +/* + * cpu_idle_wait - Used to ensure that all the CPUs discard old value of + * pm_idle and update to new pm_idle value. Required while changing pm_idle + * handler on SMP systems. + * + * Caller must have changed pm_idle to the new value before the call. Old + * pm_idle value will not be used by any CPU after the return of this function. + */ +void cpu_idle_wait(void) +{ + smp_mb(); + /* kick all the CPUs so that they exit out of pm_idle */ + smp_call_function(do_nothing, NULL, 0, 1); } EXPORT_SYMBOL_GPL(cpu_idle_wait); @@ -293,9 +297,6 @@ cpu_idle (void) #ifdef CONFIG_SMP min_xtp(); #endif - if (__get_cpu_var(cpu_idle_state)) - __get_cpu_var(cpu_idle_state) = 0; - rmb(); if (mark_idle) (*mark_idle)(1); diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 2e96f17b2f3..331d6768b5d 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -547,6 +547,129 @@ ia64_sync_user_rbs (struct task_struct *child, struct switch_stack *sw, return 0; } +static long +ia64_sync_kernel_rbs (struct task_struct *child, struct switch_stack *sw, + unsigned long user_rbs_start, unsigned long user_rbs_end) +{ + unsigned long addr, val; + long ret; + + /* now copy word for word from user rbs to kernel rbs: */ + for (addr = user_rbs_start; addr < user_rbs_end; addr += 8) { + if (access_process_vm(child, addr, &val, sizeof(val), 0) + != sizeof(val)) + return -EIO; + + ret = ia64_poke(child, sw, user_rbs_end, addr, val); + if (ret < 0) + return ret; + } + return 0; +} + +typedef long (*syncfunc_t)(struct task_struct *, struct switch_stack *, + unsigned long, unsigned long); + +static void do_sync_rbs(struct unw_frame_info *info, void *arg) +{ + struct pt_regs *pt; + unsigned long urbs_end; + syncfunc_t fn = arg; + + if (unw_unwind_to_user(info) < 0) + return; + pt = task_pt_regs(info->task); + urbs_end = ia64_get_user_rbs_end(info->task, pt, NULL); + + fn(info->task, info->sw, pt->ar_bspstore, urbs_end); +} + +/* + * when a thread is stopped (ptraced), debugger might change thread's user + * stack (change memory directly), and we must avoid the RSE stored in kernel + * to override user stack (user space's RSE is newer than kernel's in the + * case). To workaround the issue, we copy kernel RSE to user RSE before the + * task is stopped, so user RSE has updated data. we then copy user RSE to + * kernel after the task is resummed from traced stop and kernel will use the + * newer RSE to return to user. TIF_RESTORE_RSE is the flag to indicate we need + * synchronize user RSE to kernel. + */ +void ia64_ptrace_stop(void) +{ + if (test_and_set_tsk_thread_flag(current, TIF_RESTORE_RSE)) + return; + tsk_set_notify_resume(current); + unw_init_running(do_sync_rbs, ia64_sync_user_rbs); +} + +/* + * This is called to read back the register backing store. + */ +void ia64_sync_krbs(void) +{ + clear_tsk_thread_flag(current, TIF_RESTORE_RSE); + tsk_clear_notify_resume(current); + + unw_init_running(do_sync_rbs, ia64_sync_kernel_rbs); +} + +/* + * After PTRACE_ATTACH, a thread's register backing store area in user + * space is assumed to contain correct data whenever the thread is + * stopped. arch_ptrace_stop takes care of this on tracing stops. + * But if the child was already stopped for job control when we attach + * to it, then it might not ever get into ptrace_stop by the time we + * want to examine the user memory containing the RBS. + */ +void +ptrace_attach_sync_user_rbs (struct task_struct *child) +{ + int stopped = 0; + struct unw_frame_info info; + + /* + * If the child is in TASK_STOPPED, we need to change that to + * TASK_TRACED momentarily while we operate on it. This ensures + * that the child won't be woken up and return to user mode while + * we are doing the sync. (It can only be woken up for SIGKILL.) + */ + + read_lock(&tasklist_lock); + if (child->signal) { + spin_lock_irq(&child->sighand->siglock); + if (child->state == TASK_STOPPED && + !test_and_set_tsk_thread_flag(child, TIF_RESTORE_RSE)) { + tsk_set_notify_resume(child); + + child->state = TASK_TRACED; + stopped = 1; + } + spin_unlock_irq(&child->sighand->siglock); + } + read_unlock(&tasklist_lock); + + if (!stopped) + return; + + unw_init_from_blocked_task(&info, child); + do_sync_rbs(&info, ia64_sync_user_rbs); + + /* + * Now move the child back into TASK_STOPPED if it should be in a + * job control stop, so that SIGCONT can be used to wake it up. + */ + read_lock(&tasklist_lock); + if (child->signal) { + spin_lock_irq(&child->sighand->siglock); + if (child->state == TASK_TRACED && + (child->signal->flags & SIGNAL_STOP_STOPPED)) { + child->state = TASK_STOPPED; + } + spin_unlock_irq(&child->sighand->siglock); + } + read_unlock(&tasklist_lock); +} + static inline int thread_matches (struct task_struct *thread, unsigned long addr) { @@ -1422,6 +1545,7 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data) struct task_struct *child; struct switch_stack *sw; long ret; + struct unw_frame_info info; lock_kernel(); ret = -EPERM; @@ -1453,6 +1577,8 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data) if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); + if (!ret) + arch_ptrace_attach(child); goto out_tsk; } @@ -1481,6 +1607,11 @@ sys_ptrace (long request, pid_t pid, unsigned long addr, unsigned long data) /* write the word at location addr */ urbs_end = ia64_get_user_rbs_end(child, pt, NULL); ret = ia64_poke(child, sw, urbs_end, addr, data); + + /* Make sure user RBS has the latest data */ + unw_init_from_blocked_task(&info, child); + do_sync_rbs(&info, ia64_sync_user_rbs); + goto out_tsk; case PTRACE_PEEKUSR: @@ -1634,6 +1765,10 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3, && (current->ptrace & PT_PTRACED)) syscall_trace(); + /* copy user rbs to kernel rbs */ + if (test_thread_flag(TIF_RESTORE_RSE)) + ia64_sync_krbs(); + if (unlikely(current->audit_context)) { long syscall; int arch; @@ -1671,4 +1806,8 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3, || test_thread_flag(TIF_SINGLESTEP)) && (current->ptrace & PT_PTRACED)) syscall_trace(); + + /* copy user rbs to kernel rbs */ + if (test_thread_flag(TIF_RESTORE_RSE)) + ia64_sync_krbs(); } diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index 3ab04272097..17fda5293c6 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -49,13 +49,13 @@ EXPORT_SYMBOL(last_cli_ip); #endif static struct clocksource clocksource_itc = { - .name = "itc", - .rating = 350, - .read = itc_get_cycles, - .mask = CLOCKSOURCE_MASK(64), - .mult = 0, /*to be caluclated*/ - .shift = 16, - .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .name = "itc", + .rating = 350, + .read = itc_get_cycles, + .mask = CLOCKSOURCE_MASK(64), + .mult = 0, /*to be calculated*/ + .shift = 16, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static struct clocksource *itc_clocksource; diff --git a/arch/ia64/mm/contig.c b/arch/ia64/mm/contig.c index 7e9c275ea14..344f64eca7a 100644 --- a/arch/ia64/mm/contig.c +++ b/arch/ia64/mm/contig.c @@ -218,7 +218,7 @@ find_memory (void) /* Free all available memory, then mark bootmem-map as being in use. */ efi_memmap_walk(filter_rsvd_memory, free_bootmem); - reserve_bootmem(bootmap_start, bootmap_size); + reserve_bootmem(bootmap_start, bootmap_size, BOOTMEM_DEFAULT); find_initrd(); diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 0b567398f38..ee5e68b2af9 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -299,12 +299,12 @@ static void __init reserve_pernode_space(void) pages = bdp->node_low_pfn - (bdp->node_boot_start>>PAGE_SHIFT); size = bootmem_bootmap_pages(pages) << PAGE_SHIFT; base = __pa(bdp->node_bootmem_map); - reserve_bootmem_node(pdp, base, size); + reserve_bootmem_node(pdp, base, size, BOOTMEM_DEFAULT); /* Now the per-node space */ size = mem_data[node].pernode_size; base = __pa(mem_data[node].pernode_addr); - reserve_bootmem_node(pdp, base, size); + reserve_bootmem_node(pdp, base, size, BOOTMEM_DEFAULT); } } diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index 795180b8fd8..d4679ab55b9 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -35,6 +35,13 @@ config NO_IOPORT config NO_DMA def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + +config HZ + int + default 100 + source "init/Kconfig" diff --git a/arch/m32r/kernel/setup.c b/arch/m32r/kernel/setup.c index d64814385d7..0392112a5d7 100644 --- a/arch/m32r/kernel/setup.c +++ b/arch/m32r/kernel/setup.c @@ -177,25 +177,28 @@ static unsigned long __init setup_memory(void) */ reserve_bootmem(CONFIG_MEMORY_START + PAGE_SIZE, (PFN_PHYS(start_pfn) + bootmap_size + PAGE_SIZE - 1) - - CONFIG_MEMORY_START); + - CONFIG_MEMORY_START, + BOOTMEM_DEFAULT); /* * reserve physical page 0 - it's a special BIOS page on many boxes, * enabling clean reboots, SMP operation, laptop functions. */ - reserve_bootmem(CONFIG_MEMORY_START, PAGE_SIZE); + reserve_bootmem(CONFIG_MEMORY_START, PAGE_SIZE, BOOTMEM_DEFAULT); /* * reserve memory hole */ #ifdef CONFIG_MEMHOLE - reserve_bootmem(CONFIG_MEMHOLE_START, CONFIG_MEMHOLE_SIZE); + reserve_bootmem(CONFIG_MEMHOLE_START, CONFIG_MEMHOLE_SIZE, + BOOTMEM_DEFAULT); #endif #ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { - reserve_bootmem(INITRD_START, INITRD_SIZE); + reserve_bootmem(INITRD_START, INITRD_SIZE, + BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET; initrd_end = initrd_start + INITRD_SIZE; printk("initrd:start[%08lx],size[%08lx]\n", @@ -366,7 +369,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c index 0e383da158e..2c03ac1d005 100644 --- a/arch/m32r/kernel/smpboot.c +++ b/arch/m32r/kernel/smpboot.c @@ -43,6 +43,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/sched.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/bootmem.h> diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index c7efdb0aefc..07c1af7dc0e 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -91,7 +91,8 @@ unsigned long __init setup_memory(void) PFN_PHYS(mp->pages)); reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(mp->start_pfn), - PFN_PHYS(mp->free_pfn - mp->start_pfn) + bootmap_size); + PFN_PHYS(mp->free_pfn - mp->start_pfn) + bootmap_size, + BOOTMEM_DEFAULT); if (max_low_pfn < max_pfn) max_low_pfn = max_pfn; @@ -104,7 +105,7 @@ unsigned long __init setup_memory(void) if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= PFN_PHYS(max_low_pfn)) { reserve_bootmem_node(NODE_DATA(0), INITRD_START, - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET; initrd_end = initrd_start + INITRD_SIZE; printk("initrd:start[%08lx],size[%08lx]\n", diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index ffabd01c45e..2b0ed89cd17 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -52,6 +52,13 @@ config NO_IOPORT config NO_DMA def_bool SUN3 +config ARCH_SUPPORTS_AOUT + def_bool y + +config HZ + int + default 100 + mainmenu "Linux/68k Kernel Configuration" source "init/Kconfig" diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c index 8dda6515887..0055a6c06f7 100644 --- a/arch/m68k/atari/stram.c +++ b/arch/m68k/atari/stram.c @@ -154,7 +154,7 @@ void __init atari_stram_reserve_pages(void *start_mem) /* always reserve first page of ST-RAM, the first 2 kB are * supervisor-only! */ if (!kernel_in_stram) - reserve_bootmem (0, PAGE_SIZE); + reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT); } diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 3ee91869521..5de4e4ed76a 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/reboot.h> #include <linux/init_task.h> #include <linux/mqueue.h> @@ -316,53 +315,6 @@ int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) EXPORT_SYMBOL(dump_fpu); /* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - struct switch_stack *sw; - -/* changed the size calculations - should hopefully work better. lbt */ - dump->magic = CMAGIC; - dump->start_code = 0; - dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; - dump->u_dsize = ((unsigned long) (current->mm->brk + - (PAGE_SIZE-1))) >> PAGE_SHIFT; - dump->u_dsize -= dump->u_tsize; - dump->u_ssize = 0; - - if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; - - dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump); - sw = ((struct switch_stack *)regs) - 1; - dump->regs.d1 = regs->d1; - dump->regs.d2 = regs->d2; - dump->regs.d3 = regs->d3; - dump->regs.d4 = regs->d4; - dump->regs.d5 = regs->d5; - dump->regs.d6 = sw->d6; - dump->regs.d7 = sw->d7; - dump->regs.a0 = regs->a0; - dump->regs.a1 = regs->a1; - dump->regs.a2 = regs->a2; - dump->regs.a3 = sw->a3; - dump->regs.a4 = sw->a4; - dump->regs.a5 = sw->a5; - dump->regs.a6 = sw->a6; - dump->regs.d0 = regs->d0; - dump->regs.orig_d0 = regs->orig_d0; - dump->regs.stkadj = regs->stkadj; - dump->regs.sr = regs->sr; - dump->regs.pc = regs->pc; - dump->regs.fmtvec = (regs->format << 12) | regs->vector; - /* dump floating point stuff */ - dump->u_fpvalid = dump_fpu (regs, &dump->m68kfp); -} -EXPORT_SYMBOL(dump_thread); - -/* * sys_execve() executes a new program. */ asmlinkage int sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp) diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index ed3a4caec62..bba650312fd 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -323,7 +323,8 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_BLK_DEV_INITRD if (m68k_ramdisk.size) { reserve_bootmem_node(__virt_to_node(phys_to_virt(m68k_ramdisk.addr)), - m68k_ramdisk.addr, m68k_ramdisk.size); + m68k_ramdisk.addr, m68k_ramdisk.size, + BOOTMEM_DEFAULT); initrd_start = (unsigned long)phys_to_virt(m68k_ramdisk.addr); initrd_end = initrd_start + m68k_ramdisk.size; printk("initrd: %08lx - %08lx\n", initrd_start, initrd_end); @@ -449,7 +450,7 @@ static void *c_next(struct seq_file *m, void *v, loff_t *pos) static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 97f556fa493..fd4858e2dd6 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -23,7 +23,6 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/module.h> -#include <linux/a.out.h> #include <linux/user.h> #include <linux/string.h> #include <linux/linkage.h> diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index 6abbbb8aac5..548a7b32163 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -64,6 +64,9 @@ config TIME_LOW_RES config NO_IOPORT def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + source "init/Kconfig" menu "Processor type and features" @@ -522,6 +525,11 @@ config 4KSTACKS running more threads on a system and also reduces the pressure on the VM subsystem for higher order allocations. +config HZ + int + default 1000 if CLEOPATRA + default 100 + comment "RAM configuration" config RAMBASE diff --git a/arch/m68knommu/kernel/setup.c b/arch/m68knommu/kernel/setup.c index 81507c53d4a..d6f0200316f 100644 --- a/arch/m68knommu/kernel/setup.c +++ b/arch/m68knommu/kernel/setup.c @@ -203,7 +203,7 @@ void __init setup_arch(char **cmdline_p) * the bootmem bitmap so we then reserve it after freeing it :-) */ free_bootmem(memory_start, memory_end - memory_start); - reserve_bootmem(memory_start, bootmap_size); + reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); /* * Get kmalloc into gear. @@ -260,7 +260,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/mips/kernel/i8259.c b/arch/mips/kernel/i8259.c index 197d7977de3..413bd1d37f5 100644 --- a/arch/mips/kernel/i8259.c +++ b/arch/mips/kernel/i8259.c @@ -338,8 +338,10 @@ void __init init_i8259_irqs(void) init_8259A(0); - for (i = I8259A_IRQ_BASE; i < I8259A_IRQ_BASE + 16; i++) + for (i = I8259A_IRQ_BASE; i < I8259A_IRQ_BASE + 16; i++) { set_irq_chip_and_handler(i, &i8259A_chip, handle_level_irq); + set_irq_probe(i); + } setup_irq(I8259A_IRQ_BASE + PIC_CASCADE_IR, &irq2); } diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index 7852c7cdf29..290d8e3a664 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -591,9 +591,9 @@ static void irix_map_prda_page(void) return; pp = (struct prda *) v; - pp->prda_sys.t_pid = current->pid; + pp->prda_sys.t_pid = task_pid_vnr(current); pp->prda_sys.t_prid = read_c0_prid(); - pp->prda_sys.t_rpid = current->pid; + pp->prda_sys.t_rpid = task_pid_vnr(current); /* We leave the rest set to zero */ } @@ -1170,11 +1170,11 @@ static int irix_core_dump(long signr, struct pt_regs *regs, struct file *file, u prstatus.pr_info.si_signo = prstatus.pr_cursig = signr; prstatus.pr_sigpend = current->pending.signal.sig[0]; prstatus.pr_sighold = current->blocked.sig[0]; - psinfo.pr_pid = prstatus.pr_pid = current->pid; - psinfo.pr_ppid = prstatus.pr_ppid = current->parent->pid; - psinfo.pr_pgrp = prstatus.pr_pgrp = task_pgrp_nr(current); - psinfo.pr_sid = prstatus.pr_sid = task_session_nr(current); - if (current->pid == current->tgid) { + psinfo.pr_pid = prstatus.pr_pid = task_pid_vnr(current); + psinfo.pr_ppid = prstatus.pr_ppid = task_pid_vnr(current->parent); + psinfo.pr_pgrp = prstatus.pr_pgrp = task_pgrp_vnr(current); + psinfo.pr_sid = prstatus.pr_sid = task_session_vnr(current); + if (thread_group_leader(current)) { /* * This is the record for the group leader. Add in the * cumulative times of previous dead threads. This total diff --git a/arch/mips/kernel/irixsig.c b/arch/mips/kernel/irixsig.c index 5b10ac133ec..0215c805a59 100644 --- a/arch/mips/kernel/irixsig.c +++ b/arch/mips/kernel/irixsig.c @@ -578,10 +578,11 @@ out: #define W_MASK (W_EXITED | W_TRAPPED | W_STOPPED | W_CONT | W_NOHANG) -asmlinkage int irix_waitsys(int type, int pid, +asmlinkage int irix_waitsys(int type, int upid, struct irix5_siginfo __user *info, int options, struct rusage __user *ru) { + struct pid *pid = NULL; int flag, retval; DECLARE_WAITQUEUE(wait, current); struct task_struct *tsk; @@ -604,6 +605,8 @@ asmlinkage int irix_waitsys(int type, int pid, if (type != IRIX_P_PID && type != IRIX_P_PGID && type != IRIX_P_ALL) return -EINVAL; + if (type != IRIX_P_ALL) + pid = find_get_pid(upid); add_wait_queue(¤t->signal->wait_chldexit, &wait); repeat: flag = 0; @@ -612,9 +615,9 @@ repeat: tsk = current; list_for_each(_p, &tsk->children) { p = list_entry(_p, struct task_struct, sibling); - if ((type == IRIX_P_PID) && p->pid != pid) + if ((type == IRIX_P_PID) && task_pid(p) != pid) continue; - if ((type == IRIX_P_PGID) && task_pgrp_nr(p) != pid) + if ((type == IRIX_P_PGID) && task_pgrp(p) != pid) continue; if ((p->exit_signal != SIGCHLD)) continue; @@ -639,7 +642,7 @@ repeat: retval = __put_user(SIGCHLD, &info->sig); retval |= __put_user(0, &info->code); - retval |= __put_user(p->pid, &info->stuff.procinfo.pid); + retval |= __put_user(task_pid_vnr(p), &info->stuff.procinfo.pid); retval |= __put_user((p->exit_code >> 8) & 0xff, &info->stuff.procinfo.procdata.child.status); retval |= __put_user(p->utime, &info->stuff.procinfo.procdata.child.utime); @@ -657,7 +660,7 @@ repeat: getrusage(p, RUSAGE_BOTH, ru); retval = __put_user(SIGCHLD, &info->sig); retval |= __put_user(1, &info->code); /* CLD_EXITED */ - retval |= __put_user(p->pid, &info->stuff.procinfo.pid); + retval |= __put_user(task_pid_vnr(p), &info->stuff.procinfo.pid); retval |= __put_user((p->exit_code >> 8) & 0xff, &info->stuff.procinfo.procdata.child.status); retval |= __put_user(p->utime, @@ -665,7 +668,7 @@ repeat: retval |= __put_user(p->stime, &info->stuff.procinfo.procdata.child.stime); if (retval) - return retval; + goto end_waitsys; if (p->real_parent != p->parent) { write_lock_irq(&tasklist_lock); @@ -698,6 +701,7 @@ repeat: end_waitsys: current->state = TASK_RUNNING; remove_wait_queue(¤t->signal->wait_chldexit, &wait); + put_pid(pid); return retval; } diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index d06e9c9af79..e3309ff9ece 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -145,6 +145,11 @@ __setup("nokgdb", nokgdb); void __init init_IRQ(void) { + int i; + + for (i = 0; i < NR_IRQS; i++) + set_irq_noprobe(i); + arch_init_irq(); #ifdef CONFIG_KGDB diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index c032409cba9..39f3dfe134f 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -232,7 +232,7 @@ static void __init finalize_initrd(void) goto disable; } - reserve_bootmem(__pa(initrd_start), size); + reserve_bootmem(__pa(initrd_start), size, BOOTMEM_DEFAULT); initrd_below_start_ok = 1; printk(KERN_INFO "Initial ramdisk at: 0x%lx (%lu bytes)\n", @@ -413,7 +413,7 @@ static void __init bootmem_init(void) /* * Reserve the bootmap memory. */ - reserve_bootmem(PFN_PHYS(mapstart), bootmap_size); + reserve_bootmem(PFN_PHYS(mapstart), bootmap_size, BOOTMEM_DEFAULT); /* * Reserve initrd memory if needed. diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index 22fd41e946b..d70c4e0e85f 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -582,8 +582,8 @@ out: asmlinkage int irix_getpid(struct pt_regs *regs) { - regs->regs[3] = current->real_parent->pid; - return current->pid; + regs->regs[3] = task_pid_vnr(current->real_parent); + return task_pid_vnr(current); } asmlinkage int irix_getuid(struct pt_regs *regs) @@ -763,11 +763,11 @@ asmlinkage int irix_setpgrp(int flags) printk("[%s:%d] setpgrp(%d) ", current->comm, current->pid, flags); #endif if(!flags) - error = task_pgrp_nr(current); + error = task_pgrp_vnr(current); else error = sys_setsid(); #ifdef DEBUG_PROCGRPS - printk("returning %d\n", task_pgrp_nr(current)); + printk("returning %d\n", error); #endif return error; @@ -1093,10 +1093,10 @@ asmlinkage int irix_BSDsetpgrp(int pid, int pgrp) pid, pgrp); #endif if(!pid) - pid = current->pid; + pid = task_pid_vnr(current); /* Wheee, weird sysv thing... */ - if ((pgrp == 0) && (pid == current->pid)) + if ((pgrp == 0) && (pid == task_pid_vnr(current))) error = sys_setsid(); else error = sys_setpgid(pid, pgrp); diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index e5e023f50a0..bf438d02366 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -465,7 +465,8 @@ static void __init node_mem_init(cnodeid_t node) free_bootmem_node(NODE_DATA(node), slot_firstpfn << PAGE_SHIFT, (slot_lastpfn - slot_firstpfn) << PAGE_SHIFT); reserve_bootmem_node(NODE_DATA(node), slot_firstpfn << PAGE_SHIFT, - ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size); + ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size, + BOOTMEM_DEFAULT); } /* diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig new file mode 100644 index 00000000000..eedc3a5e0d9 --- /dev/null +++ b/arch/mn10300/Kconfig @@ -0,0 +1,381 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/kconfig-language.txt. +# + +mainmenu "Linux Kernel Configuration" + +config MN10300 + def_bool y + +config AM33 + def_bool y + +config MMU + def_bool y + +config HIGHMEM + def_bool n + +config NUMA + def_bool n + +config UID16 + def_bool y + +config RWSEM_GENERIC_SPINLOCK + def_bool y + +config RWSEM_XCHGADD_ALGORITHM + bool + +config GENERIC_HARDIRQS_NO__DO_IRQ + def_bool y + +config GENERIC_CALIBRATE_DELAY + def_bool y + +config GENERIC_FIND_NEXT_BIT + def_bool y + +config GENERIC_HWEIGHT + def_bool y + +config GENERIC_TIME + def_bool y + +config GENERIC_BUG + def_bool y + +config QUICKLIST + def_bool y + +config ARCH_HAS_ILOG2_U32 + def_bool y + +config ARCH_SUPPORTS_AOUT + def_bool n + +# Use the generic interrupt handling code in kernel/irq/ +config GENERIC_HARDIRQS + def_bool y + +config HOTPLUG_CPU + def_bool n + +mainmenu "Matsushita MN10300/AM33 Kernel Configuration" + +source "init/Kconfig" + + +menu "Matsushita MN10300 system setup" + +choice + prompt "Unit type" + default MN10300_UNIT_ASB2303 + help + This option specifies board for which the kernel will be + compiled. It affects the external peripherals catered for. + +config MN10300_UNIT_ASB2303 + bool "ASB2303" + +config MN10300_UNIT_ASB2305 + bool "ASB2305" + +endchoice + +choice + prompt "Processor support" + default MN10300_PROC_MN103E010 + help + This option specifies the processor for which the kernel will be + compiled. It affects the on-chip peripherals catered for. + +config MN10300_PROC_MN103E010 + bool "MN103E010" + depends on MN10300_UNIT_ASB2303 || MN10300_UNIT_ASB2305 + select MN10300_PROC_HAS_TTYSM0 + select MN10300_PROC_HAS_TTYSM1 + select MN10300_PROC_HAS_TTYSM2 + +endchoice + +choice + prompt "Processor core support" + default MN10300_CPU_AM33V2 + help + This option specifies the processor core for which the kernel will be + compiled. It affects the instruction set used. + +config MN10300_CPU_AM33V2 + bool "AM33v2" + +endchoice + +config FPU + bool "FPU present" + default y + depends on MN10300_PROC_MN103E010 + +choice + prompt "CPU Caching mode" + default MN10300_CACHE_WBACK + help + This option determines the caching mode for the kernel. + + Write-Back caching mode involves the all reads and writes causing + the affected cacheline to be read into the cache first before being + operated upon. Memory is not then updated by a write until the cache + is filled and a cacheline needs to be displaced from the cache to + make room. Only at that point is it written back. + + Write-Through caching only fetches cachelines from memory on a + read. Writes always get written directly to memory. If the affected + cacheline is also in cache, it will be updated too. + + The final option is to turn of caching entirely. + +config MN10300_CACHE_WBACK + bool "Write-Back" + +config MN10300_CACHE_WTHRU + bool "Write-Through" + +config MN10300_CACHE_DISABLED + bool "Disabled" + +endchoice + +menu "Memory layout options" + +config KERNEL_RAM_BASE_ADDRESS + hex "Base address of kernel RAM" + default "0x90000000" + +config INTERRUPT_VECTOR_BASE + hex "Base address of vector table" + default "0x90000000" + help + The base address of the vector table will be programmed into + the TBR register. It must be on 16MiB address boundary. + +config KERNEL_TEXT_ADDRESS + hex "Base address of kernel" + default "0x90001000" + +config KERNEL_ZIMAGE_BASE_ADDRESS + hex "Base address of compressed vmlinux image" + default "0x90700000" + +endmenu + +config PREEMPT + bool "Preemptible Kernel" + help + This option reduces the latency of the kernel when reacting to + real-time or interactive events by allowing a low priority process to + be preempted even if it is in kernel mode executing a system call. + This allows applications to run more reliably even when the system is + under load. + + Say Y here if you are building a kernel for a desktop, embedded + or real-time system. Say N if you are unsure. + +config PREEMPT_BKL + bool "Preempt The Big Kernel Lock" + depends on PREEMPT + default y + help + This option reduces the latency of the kernel by making the + big kernel lock preemptible. + + Say Y here if you are building a kernel for a desktop system. + Say N if you are unsure. + +config MN10300_CURRENT_IN_E2 + bool "Hold current task address in E2 register" + default y + help + This option removes the E2/R2 register from the set available to gcc + for normal use and instead uses it to store the address of the + current process's task_struct whilst in the kernel. + + This means the kernel doesn't need to calculate the address each time + "current" is used (take SP, AND with mask and dereference pointer + just to get the address), and instead can just use E2+offset + addressing each time. + + This has no effect on userspace. + +config MN10300_USING_JTAG + bool "Using JTAG to debug kernel" + default y + help + This options indicates that JTAG will be used to debug the kernel. It + suppresses the use of certain hardware debugging features, such as + single-stepping, which are taken over completely by the JTAG unit. + +config MN10300_RTC + bool "Using MN10300 RTC" + depends on MN10300_PROC_MN103E010 + default n + help + + This option enables support for the RTC, thus enabling time to be + tracked, even when system is powered down. This is available on-chip + on the MN103E010. + +config MN10300_WD_TIMER + bool "Using MN10300 watchdog timer" + default y + help + This options indicates that the watchdog timer will be used. + +config PCI + bool "Use PCI" + depends on MN10300_UNIT_ASB2305 + default y + help + Some systems (such as the ASB2305) have PCI onboard. If you have one + of these boards and you wish to use the PCI facilities, say Y here. + + The PCI-HOWTO, available from + <http://www.tldp.org/docs.html#howto>, contains valuable + information about which PCI hardware does work under Linux and which + doesn't. + +source "drivers/pci/Kconfig" + +source "drivers/pcmcia/Kconfig" + +menu "MN10300 internal serial options" + +config MN10300_PROC_HAS_TTYSM0 + bool + default n + +config MN10300_PROC_HAS_TTYSM1 + bool + default n + +config MN10300_PROC_HAS_TTYSM2 + bool + default n + +config MN10300_TTYSM + bool "Support for ttySM serial ports" + depends on MN10300 + default y + select SERIAL_CORE + help + This option enables support for the on-chip serial ports that the + MN10300 has available. + +config MN10300_TTYSM_CONSOLE + bool "Support for console on ttySM serial ports" + depends on MN10300_TTYSM + select SERIAL_CORE_CONSOLE + help + This option enables support for a console on the on-chip serial ports + that the MN10300 has available. + +# +# /dev/ttySM0 +# +config MN10300_TTYSM0 + bool "Enable SIF0 (/dev/ttySM0)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM0 + help + Enable access to SIF0 through /dev/ttySM0 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF0" + default MN10300_TTYSM0_TIMER8 + depends on MN10300_TTYSM0 + +config MN10300_TTYSM0_TIMER8 + bool "Use timer 8 (16-bit)" + +config MN10300_TTYSM0_TIMER2 + bool "Use timer 2 (8-bit)" + +endchoice + +# +# /dev/ttySM1 +# +config MN10300_TTYSM1 + bool "Enable SIF1 (/dev/ttySM1)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM1 + help + Enable access to SIF1 through /dev/ttySM1 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF1" + default MN10300_TTYSM0_TIMER9 + depends on MN10300_TTYSM1 + +config MN10300_TTYSM1_TIMER9 + bool "Use timer 9 (16-bit)" + +config MN10300_TTYSM1_TIMER3 + bool "Use timer 3 (8-bit)" + +endchoice + +# +# /dev/ttySM2 +# +config MN10300_TTYSM2 + bool "Enable SIF2 (/dev/ttySM2)" + depends on MN10300_TTYSM && MN10300_PROC_HAS_TTYSM2 + help + Enable access to SIF2 through /dev/ttySM2 or gdb-stub + +choice + prompt "Select the timer to supply the clock for SIF2" + default MN10300_TTYSM0_TIMER10 + depends on MN10300_TTYSM2 + +config MN10300_TTYSM2_TIMER10 + bool "Use timer 10 (16-bit)" + +endchoice + +config MN10300_TTYSM2_CTS + bool "Enable the use of the CTS line /dev/ttySM2" + depends on MN10300_TTYSM2 + +endmenu + +source "mm/Kconfig" + +menu "Power management options" +source kernel/power/Kconfig +endmenu + +endmenu + + +menu "Executable formats" + +source "fs/Kconfig.binfmt" + +endmenu + +source "net/Kconfig" + +source "drivers/Kconfig" + +source "fs/Kconfig" + +source "arch/mn10300/Kconfig.debug" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" + +source "arch/mn10300/oprofile/Kconfig" diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug new file mode 100644 index 00000000000..524e33819f3 --- /dev/null +++ b/arch/mn10300/Kconfig.debug @@ -0,0 +1,135 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config DEBUG_STACKOVERFLOW + bool "Check for stack overflows" + depends on DEBUG_KERNEL + +config DEBUG_DECOMPRESS_KERNEL + bool "Using serial port during decompressing kernel" + depends on DEBUG_KERNEL + default n + help + If you say Y here you will confirm the start and the end of + decompressing Linux seeing "Uncompressing Linux... " and + "Ok, booting the kernel.\n" on console. + +config KPROBES + bool "Kprobes" + depends on DEBUG_KERNEL + help + Kprobes allows you to trap at almost any kernel address and + execute a callback function. register_kprobe() establishes + a probepoint and specifies the callback. Kprobes is useful + for kernel debugging, non-intrusive instrumentation and testing. + If in doubt, say "N". + +config GDBSTUB + bool "Remote GDB kernel debugging" + depends on DEBUG_KERNEL + select DEBUG_INFO + select FRAME_POINTER + help + If you say Y here, it will be possible to remotely debug the kernel + using gdb. This enlarges your kernel ELF image disk size by several + megabytes and requires a machine with more than 16 MB, better 32 MB + RAM to avoid excessive linking time. This is only useful for kernel + hackers. If unsure, say N. + +config GDBSTUB_IMMEDIATE + bool "Break into GDB stub immediately" + depends on GDBSTUB + help + If you say Y here, GDB stub will break into the program as soon as + possible, leaving the program counter at the beginning of + start_kernel() in init/main.c. + +config GDB_CONSOLE + bool "Console output to GDB" + depends on GDBSTUB + help + If you are using GDB for remote debugging over a serial port and + would like kernel messages to be formatted into GDB $O packets so + that GDB prints them as program output, say 'Y'. + +config GDBSTUB_DEBUGGING + bool "Debug GDB stub by messages to serial port" + depends on GDBSTUB + help + This causes debugging messages to be displayed at various points + during execution of the GDB stub routines. Such messages will be + displayed on ttyS0 if that isn't the GDB stub's port, or ttySM0 + otherwise. + +config GDBSTUB_DEBUG_ENTRY + bool "Debug GDB stub entry" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about entry to or exit + from the main GDB stub routine. + +config GDBSTUB_DEBUG_PROTOCOL + bool "Debug GDB stub protocol" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about the GDB remote + protocol messages generated exchanged with GDB. + +config GDBSTUB_DEBUG_IO + bool "Debug GDB stub I/O" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about GDB stub's + low-level I/O. + +config GDBSTUB_DEBUG_BREAKPOINT + bool "Debug GDB stub breakpoint management" + depends on GDBSTUB_DEBUGGING + help + This option causes information to be displayed about GDB stub's + breakpoint management. + +choice + prompt "GDB stub port" + default GDBSTUB_TTYSM0 + depends on GDBSTUB + help + Select the serial port used for GDB-stub. + +config GDBSTUB_ON_TTYSM0 + bool "/dev/ttySM0 [SIF0]" + depends on MN10300_TTYSM0 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYSM1 + bool "/dev/ttySM1 [SIF1]" + depends on MN10300_TTYSM1 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYSM2 + bool "/dev/ttySM2 [SIF2]" + depends on MN10300_TTYSM2 + select GDBSTUB_ON_TTYSMx + +config GDBSTUB_ON_TTYS0 + bool "/dev/ttyS0" + select GDBSTUB_ON_TTYSx + +config GDBSTUB_ON_TTYS1 + bool "/dev/ttyS1" + select GDBSTUB_ON_TTYSx + +endchoice + +config GDBSTUB_ON_TTYSMx + bool + depends on GDBSTUB_ON_TTYSM0 || GDBSTUB_ON_TTYSM1 || GDBSTUB_ON_TTYSM2 + default y + +config GDBSTUB_ON_TTYSx + bool + depends on GDBSTUB_ON_TTYS0 || GDBSTUB_ON_TTYS1 + default y + +endmenu diff --git a/arch/mn10300/Makefile b/arch/mn10300/Makefile new file mode 100644 index 00000000000..6673a28ec07 --- /dev/null +++ b/arch/mn10300/Makefile @@ -0,0 +1,135 @@ +############################################################################### +# +# MN10300 Kernel makefile system specifications +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### + +KBUILD_DEFCONFIG := asb2303_defconfig + +CCSPECS := $(shell $(CC) -v 2>&1 | grep "^Reading specs from " | head -1 | cut -c20-) +CCDIR := $(strip $(patsubst %/specs,%,$(CCSPECS))) +KBUILD_CPPFLAGS += -nostdinc -I$(CCDIR)/include + +LDFLAGS := +OBJCOPYFLAGS := -O binary -R .note -R .comment -S +#LDFLAGS_vmlinux := -Map linkmap.txt +CHECKFLAGS += + +PROCESSOR := unset +UNIT := unset + +KBUILD_CFLAGS += -mam33 -mmem-funcs -DCPU=AM33 +KBUILD_AFLAGS += -mam33 -DCPU=AM33 + +ifeq ($(CONFIG_MN10300_CURRENT_IN_E2),y) +KBUILD_CFLAGS += -ffixed-e2 -fcall-saved-e5 +endif + +ifeq ($(CONFIG_MN10300_PROC_MN103E010),y) +PROCESSOR := mn103e010 +endif + +ifeq ($(CONFIG_MN10300_UNIT_ASB2303),y) +UNIT := asb2303 +endif +ifeq ($(CONFIG_MN10300_UNIT_ASB2305),y) +UNIT := asb2305 +endif + + +head-y := arch/mn10300/kernel/head.o arch/mn10300/kernel/init_task.o + +core-y += arch/mn10300/kernel/ arch/mn10300/mm/ + +ifneq ($(PROCESSOR),unset) +core-y += arch/mn10300/proc-$(PROCESSOR)/ +endif +ifneq ($(UNIT),unset) +core-y += arch/mn10300/unit-$(UNIT)/ +endif +libs-y += arch/mn10300/lib/ + +drivers-$(CONFIG_OPROFILE) += arch/mn10300/oprofile/ + +boot := arch/mn10300/boot + +.PHONY: zImage + +KBUILD_IMAGE := $(boot)/zImage +CLEAN_FILES += $(boot)/zImage +CLEAN_FILES += $(boot)/compressed/vmlinux +CLEAN_FILES += $(boot)/compressed/vmlinux.bin +CLEAN_FILES += $(boot)/compressed/vmlinux.bin.gz + +zImage: vmlinux + $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ + +all: zImage + +bootstrap: + $(Q)$(MAKEBOOT) bootstrap + +archclean: + $(Q)$(MAKE) $(clean)=arch/mn10300/proc-mn103e010 + $(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2303 + $(Q)$(MAKE) $(clean)=arch/mn10300/unit-asb2305 + +define archhelp + echo '* zImage - Compressed kernel image (arch/$(ARCH)/boot/zImage)' +endef + +# If you make sure the .S files get compiled with debug info, +# uncomment the following to disable optimisations +# that are unhelpful whilst debugging. +ifdef CONFIG_DEBUG_INFO +#KBUILD_CFLAGS += -O1 +KBUILD_AFLAGS += -Wa,--gdwarf2 +endif + +################################################################################################### +# +# juggle some symlinks in the MN10300 asm include dir +# +# Update machine proc and unit symlinks if something which affects +# them changed. We use .proc / .unit to indicate when they were +# updated last, otherwise make uses the target directory mtime. +# +################################################################################################### + +# processor specific definitions +include/asm-mn10300/.proc: $(wildcard include/config/proc/*.h) include/config/auto.conf + @echo ' SYMLINK include/asm-mn10300/proc -> include/asm-mn10300/proc-$(PROCESSOR)' +ifneq ($(KBUILD_SRC),) + $(Q)mkdir -p include/asm-mn10300 + $(Q)ln -fsn $(srctree)/include/asm-mn10300/proc-$(PROCESSOR) include/asm-mn10300/proc +else + $(Q)ln -fsn proc-$(PROCESSOR) include/asm-mn10300/proc +endif + @touch $@ + +CLEAN_FILES += include/asm-mn10300/proc include/asm-mn10300/.proc + +prepare: include/asm-mn10300/.proc + +# unit specific definitions +include/asm-mn10300/.unit: $(wildcard include/config/unit/*.h) include/config/auto.conf + @echo ' SYMLINK include/asm-mn10300/unit -> include/asm-mn10300/unit-$(UNIT)' +ifneq ($(KBUILD_SRC),) + $(Q)mkdir -p include/asm-mn10300 + $(Q)ln -fsn $(srctree)/include/asm-mn10300/unit-$(UNIT) include/asm-mn10300/unit +else + $(Q)ln -fsn unit-$(UNIT) include/asm-mn10300/unit +endif + @touch $@ + +CLEAN_FILES += include/asm-mn10300/unit include/asm-mn10300/.unit + +prepare: include/asm-mn10300/.unit diff --git a/arch/mn10300/boot/.gitignore b/arch/mn10300/boot/.gitignore new file mode 100644 index 00000000000..b6718de2369 --- /dev/null +++ b/arch/mn10300/boot/.gitignore @@ -0,0 +1 @@ +zImage diff --git a/arch/mn10300/boot/Makefile b/arch/mn10300/boot/Makefile new file mode 100644 index 00000000000..36c9caf8ea0 --- /dev/null +++ b/arch/mn10300/boot/Makefile @@ -0,0 +1,28 @@ +# MN10300 kernel compressor and wrapper +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# + +targets := vmlinux.bin zImage + +subdir- := compressed + +# --------------------------------------------------------------------------- + + +$(obj)/zImage: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + @echo 'Kernel: $@ is ready' + +$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/compressed/vmlinux: FORCE + $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@ diff --git a/arch/mn10300/boot/compressed/Makefile b/arch/mn10300/boot/compressed/Makefile new file mode 100644 index 00000000000..08a95e17168 --- /dev/null +++ b/arch/mn10300/boot/compressed/Makefile @@ -0,0 +1,22 @@ +# +# Create a compressed vmlinux image from the original vmlinux +# + +targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o + +LDFLAGS_vmlinux := -Ttext $(CONFIG_KERNEL_ZIMAGE_BASE_ADDRESS) -e startup_32 + +$(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE + $(call if_changed,ld) + @: + +$(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + +$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE + $(call if_changed,gzip) + +LDFLAGS_piggy.o := -r --format binary --oformat elf32-am33lin -T + +$(obj)/piggy.o: $(obj)/vmlinux.lds $(obj)/vmlinux.bin.gz FORCE + $(call if_changed,ld) diff --git a/arch/mn10300/boot/compressed/head.S b/arch/mn10300/boot/compressed/head.S new file mode 100644 index 00000000000..502e1eb5670 --- /dev/null +++ b/arch/mn10300/boot/compressed/head.S @@ -0,0 +1,86 @@ +/* Boot entry point for a compressed MN10300 kernel + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + .section .text + +#define DEBUG + +#include <linux/linkage.h> +#include <asm/cpu-regs.h> + + .globl startup_32 +startup_32: + # first save off parameters from bootloader + mov param_save_area,a0 + mov d0,(a0) + mov d1,(4,a0) + mov d2,(8,a0) + + mov sp,a3 + mov decomp_stack+0x2000-4,a0 + mov a0,sp + + # invalidate and enable both of the caches + mov CHCTR,a0 + clr d0 + movhu d0,(a0) # turn off first + mov CHCTR_ICINV|CHCTR_DCINV,d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD,d0 # writethru dcache + movhu d0,(a0) # enable + + # clear the BSS area + mov __bss_start,a0 + mov _end,a1 + clr d0 +bssclear: + cmp a1,a0 + bge bssclear_end + movbu d0,(a0) + inc a0 + bra bssclear +bssclear_end: + + # decompress the kernel + call decompress_kernel[],0 + + # disable caches again + mov CHCTR,a0 + clr d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + + mov param_save_area,a0 + mov (a0),d0 + mov (4,a0),d1 + mov (8,a0),d2 + + mov a3,sp + mov CONFIG_KERNEL_TEXT_ADDRESS,a0 + jmp (a0) + + .data + .align 4 +param_save_area: + .rept 3 + .word 0 + .endr + + .section .bss + .align 4 +decomp_stack: + .space 0x2000 diff --git a/arch/mn10300/boot/compressed/misc.c b/arch/mn10300/boot/compressed/misc.c new file mode 100644 index 00000000000..ded207efc97 --- /dev/null +++ b/arch/mn10300/boot/compressed/misc.c @@ -0,0 +1,429 @@ +/* MN10300 Miscellaneous helper routines for kernel decompressor + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * - Derived from arch/x86/boot/compressed/misc_32.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/compiler.h> +#include <asm/serial-regs.h> +#include "misc.h" + +#ifndef CONFIG_GDBSTUB_ON_TTYSx +/* display 'Uncompressing Linux... ' messages on ttyS0 or ttyS1 */ +#if 1 /* ttyS0 */ +#define CYG_DEV_BASE 0xA6FB0000 +#else /* ttyS1 */ +#define CYG_DEV_BASE 0xA6FC0000 +#endif + +#define CYG_DEV_THR (*((volatile __u8*)(CYG_DEV_BASE + 0x00))) +#define CYG_DEV_MCR (*((volatile __u8*)(CYG_DEV_BASE + 0x10))) +#define SIO_MCR_DTR 0x01 +#define SIO_MCR_RTS 0x02 +#define CYG_DEV_LSR (*((volatile __u8*)(CYG_DEV_BASE + 0x14))) +#define SIO_LSR_THRE 0x20 /* transmitter holding register empty */ +#define SIO_LSR_TEMT 0x40 /* transmitter register empty */ +#define CYG_DEV_MSR (*((volatile __u8*)(CYG_DEV_BASE + 0x18))) +#define SIO_MSR_CTS 0x10 /* clear to send */ +#define SIO_MSR_DSR 0x20 /* data set ready */ + +#define LSR_WAIT_FOR(STATE) \ + do { while (!(CYG_DEV_LSR & SIO_LSR_##STATE)) {} } while (0) +#define FLOWCTL_QUERY(LINE) \ + ({ CYG_DEV_MSR & SIO_MSR_##LINE; }) +#define FLOWCTL_WAIT_FOR(LINE) \ + do { while (!(CYG_DEV_MSR & SIO_MSR_##LINE)) {} } while (0) +#define FLOWCTL_CLEAR(LINE) \ + do { CYG_DEV_MCR &= ~SIO_MCR_##LINE; } while (0) +#define FLOWCTL_SET(LINE) \ + do { CYG_DEV_MCR |= SIO_MCR_##LINE; } while (0) +#endif + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +#undef memset +#undef memcpy + +static inline void *memset(const void *s, int c, size_t n) +{ + int i; + char *ss = (char *) s; + + for (i = 0; i < n; i++) + ss[i] = c; + return (void *)s; +} + +#define memzero(s, n) memset((s), 0, (n)) + +static inline void *memcpy(void *__dest, const void *__src, size_t __n) +{ + int i; + const char *s = __src; + char *d = __dest; + + for (i = 0; i < __n; i++) + d[i] = s[i]; + return __dest; +} + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, and a power of + * two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* sliding window buffer */ + +static unsigned insize; /* valid bytes in inbuf */ +static unsigned inptr; /* index of next byte to be processed in inbuf */ +static unsigned outcnt; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond, msg) { if (!(cond)) error(msg); } +# define Trace(x) fprintf x +# define Tracev(x) { if (verbose) fprintf x ; } +# define Tracevv(x) { if (verbose > 1) fprintf x ; } +# define Tracec(c, x) { if (verbose && (c)) fprintf x ; } +# define Tracecv(c, x) { if (verbose > 1 && (c)) fprintf x ; } +#else +# define Assert(cond, msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c, x) +# define Tracecv(c, x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(const char *) __attribute__((noreturn)); +static void kputs(const char *); + +static inline unsigned char get_byte(void) +{ + unsigned char ch = inptr < insize ? inbuf[inptr++] : fill_inbuf(); + +#if 0 + char hex[3]; + hex[0] = ((ch & 0x0f) > 9) ? + ((ch & 0x0f) + 'A' - 0xa) : ((ch & 0x0f) + '0'); + hex[1] = ((ch >> 4) > 9) ? + ((ch >> 4) + 'A' - 0xa) : ((ch >> 4) + '0'); + hex[2] = 0; + kputs(hex); +#endif + return ch; +} + +/* + * This is set up by the setup-routine at boot-time + */ +#define EXT_MEM_K (*(unsigned short *)0x90002) +#ifndef STANDARD_MEMORY_BIOS_CALL +#define ALT_MEM_K (*(unsigned long *) 0x901e0) +#endif +#define SCREEN_INFO (*(struct screen_info *)0x90000) + +static long bytes_out; +static uch *output_data; +static unsigned long output_ptr; + + +static void *malloc(int size); + +static inline void free(void *where) +{ /* Don't care */ +} + +static unsigned long free_mem_ptr = (unsigned long) &end; +static unsigned long free_mem_end_ptr = (unsigned long) &end + 0x90000; + +static inline void gzip_mark(void **ptr) +{ + kputs("."); + *ptr = (void *) free_mem_ptr; +} + +static inline void gzip_release(void **ptr) +{ + free_mem_ptr = (unsigned long) *ptr; +} + +#define INPLACE_MOVE_ROUTINE 0x1000 +#define LOW_BUFFER_START 0x2000 +#define LOW_BUFFER_END 0x90000 +#define LOW_BUFFER_SIZE (LOW_BUFFER_END - LOW_BUFFER_START) +#define HEAP_SIZE 0x3000 +static int high_loaded; +static uch *high_buffer_start /* = (uch *)(((ulg)&end) + HEAP_SIZE)*/; + +static char *vidmem = (char *)0xb8000; +static int lines, cols; + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size < 0) + error("Malloc error\n"); + if (!free_mem_ptr) + error("Memory error\n"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *) free_mem_ptr; + free_mem_ptr += size; + + if (free_mem_ptr >= free_mem_end_ptr) + error("\nOut of memory\n"); + + return p; +} + +static inline void scroll(void) +{ + int i; + + memcpy(vidmem, vidmem + cols * 2, (lines - 1) * cols * 2); + for (i = (lines - 1) * cols * 2; i < lines * cols * 2; i += 2) + vidmem[i] = ' '; +} + +static inline void kputchar(unsigned char ch) +{ +#ifdef CONFIG_MN10300_UNIT_ASB2305 + while (SC0STR & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + SC0TXB = 0x0d; + while (SC0STR & SC01STR_TBF) + continue; + } + + SC0TXB = ch; + +#else + while (SC1STR & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + SC1TXB = 0x0d; + while (SC1STR & SC01STR_TBF) + continue; + } + + SC1TXB = ch; + +#endif +} + +static void kputs(const char *s) +{ +#ifdef CONFIG_DEBUG_DECOMPRESS_KERNEL +#ifndef CONFIG_GDBSTUB_ON_TTYSx + char ch; + + FLOWCTL_SET(DTR); + + while (*s) { + LSR_WAIT_FOR(THRE); + + ch = *s++; + if (ch == 0x0a) { + CYG_DEV_THR = 0x0d; + LSR_WAIT_FOR(THRE); + } + CYG_DEV_THR = ch; + } + + FLOWCTL_CLEAR(DTR); +#else + + for (; *s; s++) + kputchar(*s); + +#endif +#endif /* CONFIG_DEBUG_DECOMPRESS_KERNEL */ +} + +/* =========================================================================== + * Fill the input buffer. This is called only when the buffer is empty + * and at least one byte is really needed. + */ +static int fill_inbuf() +{ + if (insize != 0) + error("ran out of input data\n"); + + inbuf = input_data; + insize = input_len; + inptr = 1; + return inbuf[0]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ +static void flush_window_low(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window_high(void) +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, ch; + in = window; + for (n = 0; n < outcnt; n++) { + ch = *output_data++ = *in++; + if ((ulg) output_data == LOW_BUFFER_END) + output_data = high_buffer_start; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + outcnt = 0; +} + +static void flush_window(void) +{ + if (high_loaded) + flush_window_high(); + else + flush_window_low(); +} + +static void error(const char *x) +{ + kputs("\n\n"); + kputs(x); + kputs("\n\n -- System halted"); + + while (1) + /* Halt */; +} + +#define STACK_SIZE (4096) + +long user_stack[STACK_SIZE]; + +struct { + long *a; + short b; +} stack_start = { &user_stack[STACK_SIZE], 0 }; + +void setup_normal_output_buffer(void) +{ +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < 1024) + error("Less than 2MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < 1024) + error("Less than 2MB of memory.\n"); +#endif + output_data = (char *) 0x100000; /* Points to 1M */ +} + +struct moveparams { + uch *low_buffer_start; + int lcount; + uch *high_buffer_start; + int hcount; +}; + +void setup_output_buffer_if_we_run_high(struct moveparams *mv) +{ + high_buffer_start = (uch *)(((ulg) &end) + HEAP_SIZE); +#ifdef STANDARD_MEMORY_BIOS_CALL + if (EXT_MEM_K < (3 * 1024)) + error("Less than 4MB of memory.\n"); +#else + if ((ALT_MEM_K > EXT_MEM_K ? ALT_MEM_K : EXT_MEM_K) < (3 * 1024)) + error("Less than 4MB of memory.\n"); +#endif + mv->low_buffer_start = output_data = (char *) LOW_BUFFER_START; + high_loaded = 1; + free_mem_end_ptr = (long) high_buffer_start; + if (0x100000 + LOW_BUFFER_SIZE > (ulg) high_buffer_start) { + high_buffer_start = (uch *)(0x100000 + LOW_BUFFER_SIZE); + mv->hcount = 0; /* say: we need not to move high_buffer */ + } else { + mv->hcount = -1; + } + mv->high_buffer_start = high_buffer_start; +} + +void close_output_buffer_if_we_run_high(struct moveparams *mv) +{ + mv->lcount = bytes_out; + if (bytes_out > LOW_BUFFER_SIZE) { + mv->lcount = LOW_BUFFER_SIZE; + if (mv->hcount) + mv->hcount = bytes_out - LOW_BUFFER_SIZE; + } else { + mv->hcount = 0; + } +} + +#undef DEBUGFLAG +#ifdef DEBUGFLAG +int debugflag; +#endif + +int decompress_kernel(struct moveparams *mv) +{ +#ifdef DEBUGFLAG + while (!debugflag) + barrier(); +#endif + + output_data = (char *) CONFIG_KERNEL_TEXT_ADDRESS; + + makecrc(); + kputs("Uncompressing Linux... "); + gunzip(); + kputs("Ok, booting the kernel.\n"); + return 0; +} diff --git a/arch/mn10300/boot/compressed/misc.h b/arch/mn10300/boot/compressed/misc.h new file mode 100644 index 00000000000..da921cd172f --- /dev/null +++ b/arch/mn10300/boot/compressed/misc.h @@ -0,0 +1,18 @@ +/* Internal definitions for the MN10300 kernel decompressor + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +extern int end; + +/* + * vmlinux.lds + */ +extern char input_data[]; +extern int input_len; diff --git a/arch/mn10300/boot/compressed/vmlinux.lds b/arch/mn10300/boot/compressed/vmlinux.lds new file mode 100644 index 00000000000..a084903603f --- /dev/null +++ b/arch/mn10300/boot/compressed/vmlinux.lds @@ -0,0 +1,9 @@ +SECTIONS +{ + .data : { + input_len = .; + LONG(input_data_end - input_data) input_data = .; + *(.data) + input_data_end = .; + } +} diff --git a/arch/mn10300/boot/install.sh b/arch/mn10300/boot/install.sh new file mode 100644 index 00000000000..072951c8397 --- /dev/null +++ b/arch/mn10300/boot/install.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# +# arch/mn10300/boot/install -c.sh +# +# This file is subject to the terms and conditions of the GNU General Public +# Licence. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1995 by Linus Torvalds +# +# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin +# +# "make install -c" script for i386 architecture +# +# Arguments: +# $1 - kernel version +# $2 - kernel image file +# $3 - kernel map file +# $4 - default install -c path (blank if root directory) +# $5 - boot rom file +# + +# User may have a custom install -c script + +rm -fr $4/../usr/include/linux $4/../usr/include/asm +install -c -m 0755 $2 $4/vmlinuz +install -c -m 0755 $5 $4/boot.rom +install -c -m 0755 -d $4/../usr/include/linux +cd $TOPDIR/include/linux +for i in `find . -maxdepth 1 -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux +done +install -c -m 0755 -d $4/../usr/include/linux/byteorder +cd $TOPDIR/include/linux/byteorder +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/byteorder +done +install -c -m 0755 -d $4/../usr/include/linux/lockd +cd $TOPDIR/include/linux/lockd +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/lockd +done +install -c -m 0755 -d $4/../usr/include/linux/netfilter_ipv4 +cd $TOPDIR/include/linux/netfilter_ipv4 +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/netfilter_ipv4 +done +install -c -m 0755 -d $4/../usr/include/linux/nfsd +cd $TOPDIR/include/linux/nfsd +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/nfsd/$i +done +install -c -m 0755 -d $4/../usr/include/linux/raid +cd $TOPDIR/include/linux/raid +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/raid +done +install -c -m 0755 -d $4/../usr/include/linux/sunrpc +cd $TOPDIR/include/linux/sunrpc +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/linux/sunrpc +done +install -c -m 0755 -d $4/../usr/include/asm +cd $TOPDIR/include/asm +for i in `find . -name '*.h' -print`; do + install -c -m 0644 $i $4/../usr/include/asm +done diff --git a/arch/mn10300/boot/tools/build.c b/arch/mn10300/boot/tools/build.c new file mode 100644 index 00000000000..4f552ead0f8 --- /dev/null +++ b/arch/mn10300/boot/tools/build.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1997 Martin Mares + */ + +/* + * This file builds a disk-image from three different files: + * + * - bootsect: exactly 512 bytes of 8086 machine code, loads the rest + * - setup: 8086 machine code, sets up system parm + * - system: 80386 code for actual system + * + * It does some checking that all files are of the correct type, and + * just writes the result to stdout, removing headers and padding to + * the right amount. It also writes some system data to stderr. + */ + +/* + * Changes by tytso to allow root device specification + * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996 + * Cross compiling fixes by Gertjan van Wingerde, July 1996 + * Rewritten by Martin Mares, April 1997 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <unistd.h> +#include <fcntl.h> +#include <asm/boot.h> + +#define DEFAULT_MAJOR_ROOT 0 +#define DEFAULT_MINOR_ROOT 0 + +/* Minimal number of setup sectors (see also bootsect.S) */ +#define SETUP_SECTS 4 + +uint8_t buf[1024]; +int fd; +int is_big_kernel; + +__attribute__((noreturn)) +void die(const char *str, ...) +{ + va_list args; + va_start(args, str); + vfprintf(stderr, str, args); + fputc('\n', stderr); + exit(1); +} + +void file_open(const char *name) +{ + fd = open(name, O_RDONLY, 0); + if (fd < 0) + die("Unable to open `%s': %m", name); +} + +__attribute__((noreturn)) +void usage(void) +{ + die("Usage: build [-b] bootsect setup system [rootdev] [> image]"); +} + +int main(int argc, char **argv) +{ + unsigned int i, c, sz, setup_sectors; + uint32_t sys_size; + uint8_t major_root, minor_root; + struct stat sb; + + if (argc > 2 && !strcmp(argv[1], "-b")) { + is_big_kernel = 1; + argc--, argv++; + } + if ((argc < 4) || (argc > 5)) + usage(); + if (argc > 4) { + if (!strcmp(argv[4], "CURRENT")) { + if (stat("/", &sb)) { + perror("/"); + die("Couldn't stat /"); + } + major_root = major(sb.st_dev); + minor_root = minor(sb.st_dev); + } else if (strcmp(argv[4], "FLOPPY")) { + if (stat(argv[4], &sb)) { + perror(argv[4]); + die("Couldn't stat root device."); + } + major_root = major(sb.st_rdev); + minor_root = minor(sb.st_rdev); + } else { + major_root = 0; + minor_root = 0; + } + } else { + major_root = DEFAULT_MAJOR_ROOT; + minor_root = DEFAULT_MINOR_ROOT; + } + fprintf(stderr, "Root device is (%d, %d)\n", major_root, minor_root); + + file_open(argv[1]); + i = read(fd, buf, sizeof(buf)); + fprintf(stderr, "Boot sector %d bytes.\n", i); + if (i != 512) + die("Boot block must be exactly 512 bytes"); + if (buf[510] != 0x55 || buf[511] != 0xaa) + die("Boot block hasn't got boot flag (0xAA55)"); + buf[508] = minor_root; + buf[509] = major_root; + if (write(1, buf, 512) != 512) + die("Write call failed"); + close(fd); + + /* Copy the setup code */ + file_open(argv[2]); + for (i = 0; (c = read(fd, buf, sizeof(buf))) > 0; i += c) + if (write(1, buf, c) != c) + die("Write call failed"); + if (c != 0) + die("read-error on `setup'"); + close(fd); + + /* Pad unused space with zeros */ + setup_sectors = (i + 511) / 512; + /* for compatibility with ancient versions of LILO. */ + if (setup_sectors < SETUP_SECTS) + setup_sectors = SETUP_SECTS; + fprintf(stderr, "Setup is %d bytes.\n", i); + memset(buf, 0, sizeof(buf)); + while (i < setup_sectors * 512) { + c = setup_sectors * 512 - i; + if (c > sizeof(buf)) + c = sizeof(buf); + if (write(1, buf, c) != c) + die("Write call failed"); + i += c; + } + + file_open(argv[3]); + if (fstat(fd, &sb)) + die("Unable to stat `%s': %m", argv[3]); + sz = sb.st_size; + fprintf(stderr, "System is %d kB\n", sz / 1024); + sys_size = (sz + 15) / 16; + /* 0x28000*16 = 2.5 MB, conservative estimate for the current maximum */ + if (sys_size > (is_big_kernel ? 0x28000 : DEF_SYSSIZE)) + die("System is too big. Try using %smodules.", + is_big_kernel ? "" : "bzImage or "); + if (sys_size > 0xffff) + fprintf(stderr, + "warning: kernel is too big for standalone boot " + "from floppy\n"); + while (sz > 0) { + int l, n; + + l = (sz > sizeof(buf)) ? sizeof(buf) : sz; + n = read(fd, buf, l); + if (n != l) { + if (n < 0) + die("Error reading %s: %m", argv[3]); + else + die("%s: Unexpected EOF", argv[3]); + } + if (write(1, buf, l) != l) + die("Write failed"); + sz -= l; + } + close(fd); + + /* Write sizes to the bootsector */ + if (lseek(1, 497, SEEK_SET) != 497) + die("Output: seek failed"); + buf[0] = setup_sectors; + if (write(1, buf, 1) != 1) + die("Write of setup sector count failed"); + if (lseek(1, 500, SEEK_SET) != 500) + die("Output: seek failed"); + buf[0] = (sys_size & 0xff); + buf[1] = ((sys_size >> 8) & 0xff); + if (write(1, buf, 2) != 2) + die("Write of image length failed"); + + return 0; +} diff --git a/arch/mn10300/configs/asb2303_defconfig b/arch/mn10300/configs/asb2303_defconfig new file mode 100644 index 00000000000..ca9876a111d --- /dev/null +++ b/arch/mn10300/configs/asb2303_defconfig @@ -0,0 +1,558 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24-rc2 +# Fri Nov 16 13:36:38 2007 +# +CONFIG_MN10300=y +CONFIG_AM33=y +CONFIG_MMU=y +# CONFIG_HIGHMEM is not set +# CONFIG_NUMA is not set +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_BUG=y +CONFIG_QUICKLIST=y +CONFIG_ARCH_HAS_ILOG2_U32=y +# CONFIG_ARCH_SUPPORTS_AOUT is not set +CONFIG_GENERIC_HARDIRQS=y +# CONFIG_HOTPLUG_CPU is not set +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +# CONFIG_FAIR_GROUP_SCHED is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_SYSCTL_SYSCALL=y +# CONFIG_KALLSYMS is not set +# CONFIG_HOTPLUG is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +# CONFIG_BLOCK is not set + +# +# Matsushita MN10300 system setup +# +CONFIG_MN10300_UNIT_ASB2303=y +# CONFIG_MN10300_UNIT_ASB2305 is not set +CONFIG_MN10300_PROC_MN103E010=y +CONFIG_MN10300_CPU_AM33V2=y +CONFIG_FPU=y +CONFIG_MN10300_CACHE_WBACK=y +# CONFIG_MN10300_CACHE_WTHRU is not set +# CONFIG_MN10300_CACHE_DISABLED is not set + +# +# Memory layout options +# +CONFIG_KERNEL_RAM_BASE_ADDRESS=0x90000000 +CONFIG_INTERRUPT_VECTOR_BASE=0x90000000 +CONFIG_KERNEL_TEXT_ADDRESS=0x90001000 +CONFIG_KERNEL_ZIMAGE_BASE_ADDRESS=0x90700000 +CONFIG_PREEMPT=y +CONFIG_PREEMPT_BKL=y +CONFIG_MN10300_CURRENT_IN_E2=y +CONFIG_MN10300_USING_JTAG=y +CONFIG_MN10300_RTC=y +CONFIG_MN10300_WD_TIMER=y +# CONFIG_ARCH_SUPPORTS_MSI is not set + +# +# MN10300 internal serial options +# +CONFIG_MN10300_PROC_HAS_TTYSM0=y +CONFIG_MN10300_PROC_HAS_TTYSM1=y +CONFIG_MN10300_PROC_HAS_TTYSM2=y +CONFIG_MN10300_TTYSM=y +CONFIG_MN10300_TTYSM_CONSOLE=y +CONFIG_MN10300_TTYSM0=y +CONFIG_MN10300_TTYSM0_TIMER8=y +# CONFIG_MN10300_TTYSM0_TIMER2 is not set +CONFIG_MN10300_TTYSM1=y +CONFIG_MN10300_TTYSM1_TIMER9=y +# CONFIG_MN10300_TTYSM1_TIMER3 is not set +# CONFIG_MN10300_TTYSM2 is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_NR_QUICK=1 +CONFIG_VIRT_TO_BUS=y + +# +# Power management options +# +# CONFIG_PM is not set + +# +# Executable formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +CONFIG_MTD_DEBUG=y +CONFIG_MTD_DEBUG_VERBOSE=0 +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1 +CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set +# CONFIG_MTD_CMDLINE_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_GEOMETRY=y +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +CONFIG_MTD_CFI_I4=y +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_OTP is not set +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CFI_AMDSTD=y +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0x8000000 +CONFIG_MTD_PHYSMAP_LEN=0x0 +CONFIG_MTD_PHYSMAP_BANKWIDTH=2 +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +# CONFIG_MTD_NAND is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set + +# +# SCSI device support +# +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_B44 is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +# CONFIG_SERIAL_8250_MANY_PORTS is not set +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_RSA is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +CONFIG_RTC=y +# CONFIG_R3964 is not set +# CONFIG_TCG_TPM is not set +# CONFIG_I2C is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set +# CONFIG_USB_SUPPORT is not set +# CONFIG_MMC is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_RTC_CLASS is not set + +# +# Userspace I/O +# +# CONFIG_UIO is not set + +# +# File systems +# +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_NLS is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_SAMPLES is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y + +# +# Profiling support +# +CONFIG_PROFILING=y +CONFIG_OPROFILE=y diff --git a/arch/mn10300/kernel/Makefile b/arch/mn10300/kernel/Makefile new file mode 100644 index 00000000000..ef07c956170 --- /dev/null +++ b/arch/mn10300/kernel/Makefile @@ -0,0 +1,27 @@ +# +# Makefile for the MN10300-specific core kernel code +# +extra-y := head.o init_task.o vmlinux.lds + +obj-y := process.o semaphore.o signal.o entry.o fpu.o traps.o irq.o \ + ptrace.o setup.o time.o sys_mn10300.o io.o kthread.o \ + switch_to.o mn10300_ksyms.o kernel_execve.o + +obj-$(CONFIG_MN10300_WD_TIMER) += mn10300-watchdog.o mn10300-watchdog-low.o + +obj-$(CONFIG_FPU) += fpu-low.o + +obj-$(CONFIG_MN10300_TTYSM) += mn10300-serial.o mn10300-serial-low.o \ + mn10300-debug.o +obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-low.o +obj-$(CONFIG_GDBSTUB_ON_TTYSx) += gdb-io-serial.o gdb-io-serial-low.o +obj-$(CONFIG_GDBSTUB_ON_TTYSMx) += gdb-io-ttysm.o gdb-io-ttysm-low.o + +ifneq ($(CONFIG_MN10300_CACHE_DISABLED),y) +obj-$(CONFIG_GDBSTUB) += gdb-cache.o +endif + +obj-$(CONFIG_MN10300_RTC) += rtc.o +obj-$(CONFIG_PROFILE) += profile.o profile-low.o +obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_KPROBES) += kprobes.o diff --git a/arch/mn10300/kernel/asm-offsets.c b/arch/mn10300/kernel/asm-offsets.c new file mode 100644 index 00000000000..ee2d9f8af5a --- /dev/null +++ b/arch/mn10300/kernel/asm-offsets.c @@ -0,0 +1,108 @@ +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed + * to extract and format the required data. + */ + +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/personality.h> +#include <asm/ucontext.h> +#include <asm/processor.h> +#include <asm/thread_info.h> +#include <asm/ptrace.h> +#include "sigframe.h" +#include "mn10300-serial.h" + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->") + +#define OFFSET(sym, str, mem) \ + DEFINE(sym, offsetof(struct str, mem)); + +void foo(void) +{ + OFFSET(SIGCONTEXT_d0, sigcontext, d0); + OFFSET(SIGCONTEXT_d1, sigcontext, d1); + BLANK(); + + OFFSET(TI_task, thread_info, task); + OFFSET(TI_exec_domain, thread_info, exec_domain); + OFFSET(TI_flags, thread_info, flags); + OFFSET(TI_cpu, thread_info, cpu); + OFFSET(TI_preempt_count, thread_info, preempt_count); + OFFSET(TI_addr_limit, thread_info, addr_limit); + OFFSET(TI_restart_block, thread_info, restart_block); + BLANK(); + + OFFSET(REG_D0, pt_regs, d0); + OFFSET(REG_D1, pt_regs, d1); + OFFSET(REG_D2, pt_regs, d2); + OFFSET(REG_D3, pt_regs, d3); + OFFSET(REG_A0, pt_regs, a0); + OFFSET(REG_A1, pt_regs, a1); + OFFSET(REG_A2, pt_regs, a2); + OFFSET(REG_A3, pt_regs, a3); + OFFSET(REG_E0, pt_regs, e0); + OFFSET(REG_E1, pt_regs, e1); + OFFSET(REG_E2, pt_regs, e2); + OFFSET(REG_E3, pt_regs, e3); + OFFSET(REG_E4, pt_regs, e4); + OFFSET(REG_E5, pt_regs, e5); + OFFSET(REG_E6, pt_regs, e6); + OFFSET(REG_E7, pt_regs, e7); + OFFSET(REG_SP, pt_regs, sp); + OFFSET(REG_EPSW, pt_regs, epsw); + OFFSET(REG_PC, pt_regs, pc); + OFFSET(REG_LAR, pt_regs, lar); + OFFSET(REG_LIR, pt_regs, lir); + OFFSET(REG_MDR, pt_regs, mdr); + OFFSET(REG_MCVF, pt_regs, mcvf); + OFFSET(REG_MCRL, pt_regs, mcrl); + OFFSET(REG_MCRH, pt_regs, mcrh); + OFFSET(REG_MDRQ, pt_regs, mdrq); + OFFSET(REG_ORIG_D0, pt_regs, orig_d0); + OFFSET(REG_NEXT, pt_regs, next); + DEFINE(REG__END, sizeof(struct pt_regs)); + BLANK(); + + OFFSET(THREAD_UREGS, thread_struct, uregs); + OFFSET(THREAD_PC, thread_struct, pc); + OFFSET(THREAD_SP, thread_struct, sp); + OFFSET(THREAD_A3, thread_struct, a3); + OFFSET(THREAD_USP, thread_struct, usp); + OFFSET(THREAD_FRAME, thread_struct, __frame); + BLANK(); + + DEFINE(CLONE_VM_asm, CLONE_VM); + DEFINE(CLONE_FS_asm, CLONE_FS); + DEFINE(CLONE_FILES_asm, CLONE_FILES); + DEFINE(CLONE_SIGHAND_asm, CLONE_SIGHAND); + DEFINE(CLONE_UNTRACED_asm, CLONE_UNTRACED); + DEFINE(SIGCHLD_asm, SIGCHLD); + BLANK(); + + OFFSET(EXEC_DOMAIN_handler, exec_domain, handler); + OFFSET(RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext); + + DEFINE(PAGE_SIZE_asm, PAGE_SIZE); + + OFFSET(__rx_buffer, mn10300_serial_port, rx_buffer); + OFFSET(__rx_inp, mn10300_serial_port, rx_inp); + OFFSET(__rx_outp, mn10300_serial_port, rx_outp); + OFFSET(__tx_info_buffer, mn10300_serial_port, uart.info); + OFFSET(__tx_xchar, mn10300_serial_port, tx_xchar); + OFFSET(__tx_break, mn10300_serial_port, tx_break); + OFFSET(__intr_flags, mn10300_serial_port, intr_flags); + OFFSET(__rx_icr, mn10300_serial_port, rx_icr); + OFFSET(__tx_icr, mn10300_serial_port, tx_icr); + OFFSET(__tm_icr, mn10300_serial_port, _tmicr); + OFFSET(__iobase, mn10300_serial_port, _iobase); + + DEFINE(__UART_XMIT_SIZE, UART_XMIT_SIZE); + OFFSET(__xmit_buffer, uart_info, xmit.buf); + OFFSET(__xmit_head, uart_info, xmit.head); + OFFSET(__xmit_tail, uart_info, xmit.tail); +} diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S new file mode 100644 index 00000000000..11de3606eee --- /dev/null +++ b/arch/mn10300/kernel/entry.S @@ -0,0 +1,721 @@ +############################################################################### +# +# MN10300 Exception and interrupt entry points +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/system.h> +#include <asm/thread_info.h> +#include <asm/intctl-regs.h> +#include <asm/busctl-regs.h> +#include <asm/timer-regs.h> +#include <asm/unit/leds.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/errno.h> +#include <asm/asm-offsets.h> +#include <asm/frame.inc> + +#ifdef CONFIG_PREEMPT +#define preempt_stop __cli +#else +#define preempt_stop +#define resume_kernel restore_all +#endif + + .macro __cli + and ~EPSW_IM,epsw + or EPSW_IE|MN10300_CLI_LEVEL,epsw + nop + nop + nop + .endm + .macro __sti + or EPSW_IE|EPSW_IM_7,epsw + .endm + + + .am33_2 + +############################################################################### +# +# the return path for a forked child +# - on entry, D0 holds the address of the previous task to run +# +############################################################################### +ENTRY(ret_from_fork) + call schedule_tail[],0 + GET_THREAD_INFO a2 + + # return 0 to indicate child process + clr d0 + mov d0,(REG_D0,fp) + jmp syscall_exit + +############################################################################### +# +# system call handler +# +############################################################################### +ENTRY(system_call) + add -4,sp + SAVE_ALL + mov d0,(REG_ORIG_D0,fp) + GET_THREAD_INFO a2 + cmp nr_syscalls,d0 + bcc syscall_badsys + btst _TIF_SYSCALL_TRACE,(TI_flags,a2) + bne syscall_trace_entry +syscall_call: + add d0,d0,a1 + add a1,a1 + mov (REG_A0,fp),d0 + mov (sys_call_table,a1),a0 + calls (a0) + mov d0,(REG_D0,fp) +syscall_exit: + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + __cli + mov (TI_flags,a2),d2 + btst _TIF_ALLWORK_MASK,d2 + bne syscall_exit_work +restore_all: + RESTORE_ALL + +############################################################################### +# +# perform work that needs to be done immediately before resumption and syscall +# tracing +# +############################################################################### + ALIGN +syscall_exit_work: + btst _TIF_SYSCALL_TRACE,d2 + beq work_pending + __sti # could let do_syscall_trace() call + # schedule() instead + mov fp,d0 + mov 1,d1 + call do_syscall_trace[],0 # do_syscall_trace(regs,entryexit) + jmp resume_userspace + + ALIGN +work_pending: + btst _TIF_NEED_RESCHED,d2 + beq work_notifysig + +work_resched: + call schedule[],0 + + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + __cli + + # is there any work to be done other than syscall tracing? + mov (TI_flags,a2),d2 + btst _TIF_WORK_MASK,d2 + beq restore_all + btst _TIF_NEED_RESCHED,d2 + bne work_resched + + # deal with pending signals and notify-resume requests +work_notifysig: + mov fp,d0 + mov d2,d1 + call do_notify_resume[],0 + jmp resume_userspace + + # perform syscall entry tracing +syscall_trace_entry: + mov -ENOSYS,d0 + mov d0,(REG_D0,fp) + mov fp,d0 + clr d1 + call do_syscall_trace[],0 + mov (REG_ORIG_D0,fp),d0 + mov (REG_D1,fp),d1 + cmp nr_syscalls,d0 + bcs syscall_call + jmp syscall_exit + +syscall_badsys: + mov -ENOSYS,d0 + mov d0,(REG_D0,fp) + jmp resume_userspace + + # userspace resumption stub bypassing syscall exit tracing + .globl ret_from_exception, ret_from_intr + ALIGN +ret_from_exception: + preempt_stop +ret_from_intr: + GET_THREAD_INFO a2 + mov (REG_EPSW,fp),d0 # need to deliver signals before + # returning to userspace + and EPSW_nSL,d0 + beq resume_kernel # returning to supervisor mode + +ENTRY(resume_userspace) + # make sure we don't miss an interrupt setting need_resched or + # sigpending between sampling and the rti + __cli + + # is there any work to be done on int/exception return? + mov (TI_flags,a2),d2 + btst _TIF_WORK_MASK,d2 + bne work_pending + jmp restore_all + +#ifdef CONFIG_PREEMPT +ENTRY(resume_kernel) + mov (TI_preempt_count,a2),d0 # non-zero preempt_count ? + cmp 0,d0 + bne restore_all + +need_resched: + btst _TIF_NEED_RESCHED,(TI_flags,a2) + beq restore_all + mov (REG_EPSW,fp),d0 + and EPSW_IM,d0 + cmp EPSW_IM_7,d0 # interrupts off (exception path) ? + beq restore_all + call preempt_schedule_irq[],0 + jmp need_resched +#endif + + +############################################################################### +# +# IRQ handler entry point +# - intended to be entered at multiple priorities +# +############################################################################### +ENTRY(irq_handler) + add -4,sp + SAVE_ALL + + # it's not a syscall + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + mov fp,d0 + call do_IRQ[],0 # do_IRQ(regs) + + jmp ret_from_intr + +############################################################################### +# +# Monitor Signal handler entry point +# +############################################################################### +ENTRY(monitor_signal) + movbu (0xae000001),d1 + cmp 1,d1 + beq monsignal + ret [],0 + +monsignal: + or EPSW_NMID,epsw + mov d0,a0 + mov a0,sp + mov (REG_EPSW,fp),d1 + and ~EPSW_nSL,d1 + mov d1,(REG_EPSW,fp) + movm (sp),[d2,d3,a2,a3,exreg0,exreg1,exother] + mov (sp),a1 + mov a1,usp + movm (sp),[other] + add 4,sp +here: jmp 0x8e000008-here+0x8e000008 + +############################################################################### +# +# Double Fault handler entry point +# - note that there will not be a stack, D0/A0 will hold EPSW/PC as were +# +############################################################################### + .section .bss + .balign THREAD_SIZE + .space THREAD_SIZE +__df_stack: + .previous + +ENTRY(double_fault) + mov a0,(__df_stack-4) # PC as was + mov d0,(__df_stack-8) # EPSW as was + mn10300_set_dbfleds # display 'db-f' on the LEDs + mov 0xaa55aa55,d0 + mov d0,(__df_stack-12) # no ORIG_D0 + mov sp,a0 # save corrupted SP + mov __df_stack-12,sp # emergency supervisor stack + SAVE_ALL + mov a0,(REG_A0,fp) # save corrupted SP as A0 (which got + # clobbered by the CPU) + mov fp,d0 + calls do_double_fault +double_fault_loop: + bra double_fault_loop + +############################################################################### +# +# Bus Error handler entry point +# - handle external (async) bus errors separately +# +############################################################################### +ENTRY(raw_bus_error) + add -4,sp + mov d0,(sp) + mov (BCBERR),d0 # what + btst BCBERR_BEMR_DMA,d0 # see if it was an external bus error + beq __common_exception_aux # it wasn't + + SAVE_ALL + mov (BCBEAR),d1 # destination of erroneous access + + mov (REG_ORIG_D0,fp),d2 + mov d2,(REG_D0,fp) + mov -1,d2 + mov d2,(REG_ORIG_D0,fp) + + add -4,sp + mov fp,(12,sp) # frame pointer + call io_bus_error[],0 + jmp restore_all + +############################################################################### +# +# Miscellaneous exception entry points +# +############################################################################### +ENTRY(nmi_handler) + add -4,sp + mov d0,(sp) + mov (TBR),d0 + bra __common_exception_nonmi + +ENTRY(__common_exception) + add -4,sp + mov d0,(sp) + +__common_exception_aux: + mov (TBR),d0 + and ~EPSW_NMID,epsw # turn NMIs back on if not NMI + or EPSW_IE,epsw + +__common_exception_nonmi: + and 0x0000FFFF,d0 # turn the exception code into a vector + # table index + + btst 0x00000007,d0 + bne 1f + cmp 0x00000400,d0 + bge 1f + + SAVE_ALL # build the stack frame + + mov (REG_D0,fp),a2 # get the exception number + mov (REG_ORIG_D0,fp),d0 + mov d0,(REG_D0,fp) + mov -1,d0 + mov d0,(REG_ORIG_D0,fp) + +#ifdef CONFIG_GDBSTUB + btst 0x01,(gdbstub_busy) + beq 2f + and ~EPSW_IE,epsw + mov fp,d0 + mov a2,d1 + call gdbstub_exception[],0 # gdbstub itself caused an exception + bra restore_all +2: +#endif + + mov fp,d0 # arg 0: stacked register file + mov a2,d1 # arg 1: exception number + lsr 1,a2 + + mov (exception_table,a2),a2 + calls (a2) + jmp ret_from_exception + +1: pi # BUG() equivalent + +############################################################################### +# +# Exception handler functions table +# +############################################################################### + .data +ENTRY(exception_table) + .rept 0x400>>1 + .long uninitialised_exception + .endr + .previous + +############################################################################### +# +# Change an entry in the exception table +# - D0 exception code, D1 handler +# +############################################################################### +ENTRY(set_excp_vector) + lsr 1,d0 + add exception_table,d0 + mov d1,(d0) + mov 4,d1 +#if defined(CONFIG_MN10300_CACHE_WBACK) + jmp mn10300_dcache_flush_inv_range2 +#else + ret [],0 +#endif + +############################################################################### +# +# System call table +# +############################################################################### + .data +ENTRY(sys_call_table) + .long sys_restart_syscall /* 0 */ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 - old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* old sys_olduname */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long old_select + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall /* old sys_uname */ + .long sys_ni_syscall /* 110 - iopl */ + .long sys_vhangup + .long sys_ni_syscall /* old "idle" system call */ + .long sys_ni_syscall /* vm86old */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* modify_ldt */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "create_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130: old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* reserved for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_ni_syscall /* vm86 */ + .long sys_ni_syscall /* Old sys_query_module */ + .long sys_poll + .long sys_nfsservctl + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* reserved for streams1 */ + .long sys_ni_syscall /* reserved for streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall /* sys_set_thread_area */ + .long sys_ni_syscall /* sys_get_thread_area */ + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* sys_vserver */ + .long sys_mbind + .long sys_get_mempolicy /* 275 */ + .long sys_set_mempolicy + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_kexec_load + .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_cacheflush + .long sys_ioprio_set /* 290 */ + .long sys_ioprio_get + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages /* 295 */ + .long sys_openat + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat /* 300 */ + .long sys_fstatat64 + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat /* 305 */ + .long sys_readlinkat + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll /* 310 */ + .long sys_unshare + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range /* 315 */ + .long sys_tee + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait /* 320 */ + .long sys_utimensat + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_fallocate /* 325 */ + .long sys_timerfd_settime + .long sys_timerfd_gettime + + +nr_syscalls=(.-sys_call_table)/4 diff --git a/arch/mn10300/kernel/fpu-low.S b/arch/mn10300/kernel/fpu-low.S new file mode 100644 index 00000000000..96cfd47e68d --- /dev/null +++ b/arch/mn10300/kernel/fpu-low.S @@ -0,0 +1,197 @@ +/* MN10300 Low level FPU management operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cpu-regs.h> + +############################################################################### +# +# void fpu_init_state(void) +# - initialise the FPU +# +############################################################################### + .globl fpu_init_state + .type fpu_init_state,@function +fpu_init_state: + mov epsw,d0 + or EPSW_FE,epsw + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + fmov 0,fs0 + fmov fs0,fs1 + fmov fs0,fs2 + fmov fs0,fs3 + fmov fs0,fs4 + fmov fs0,fs5 + fmov fs0,fs6 + fmov fs0,fs7 + fmov fs0,fs8 + fmov fs0,fs9 + fmov fs0,fs10 + fmov fs0,fs11 + fmov fs0,fs12 + fmov fs0,fs13 + fmov fs0,fs14 + fmov fs0,fs15 + fmov fs0,fs16 + fmov fs0,fs17 + fmov fs0,fs18 + fmov fs0,fs19 + fmov fs0,fs20 + fmov fs0,fs21 + fmov fs0,fs22 + fmov fs0,fs23 + fmov fs0,fs24 + fmov fs0,fs25 + fmov fs0,fs26 + fmov fs0,fs27 + fmov fs0,fs28 + fmov fs0,fs29 + fmov fs0,fs30 + fmov fs0,fs31 + fmov FPCR_INIT,fpcr + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + mov d0,epsw + ret [],0 + + .size fpu_init_state,.-fpu_init_state + +############################################################################### +# +# void fpu_save(struct fpu_state_struct *) +# - save the fpu state +# - note that an FPU Operational exception might occur during this process +# +############################################################################### + .globl fpu_save + .type fpu_save,@function +fpu_save: + mov epsw,d1 + or EPSW_FE,epsw /* enable the FPU so we can access it */ + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + mov d0,a0 + fmov fs0,(a0+) + fmov fs1,(a0+) + fmov fs2,(a0+) + fmov fs3,(a0+) + fmov fs4,(a0+) + fmov fs5,(a0+) + fmov fs6,(a0+) + fmov fs7,(a0+) + fmov fs8,(a0+) + fmov fs9,(a0+) + fmov fs10,(a0+) + fmov fs11,(a0+) + fmov fs12,(a0+) + fmov fs13,(a0+) + fmov fs14,(a0+) + fmov fs15,(a0+) + fmov fs16,(a0+) + fmov fs17,(a0+) + fmov fs18,(a0+) + fmov fs19,(a0+) + fmov fs20,(a0+) + fmov fs21,(a0+) + fmov fs22,(a0+) + fmov fs23,(a0+) + fmov fs24,(a0+) + fmov fs25,(a0+) + fmov fs26,(a0+) + fmov fs27,(a0+) + fmov fs28,(a0+) + fmov fs29,(a0+) + fmov fs30,(a0+) + fmov fs31,(a0+) + fmov fpcr,d0 + mov d0,(a0) +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + + mov d1,epsw + ret [],0 + + .size fpu_save,.-fpu_save + +############################################################################### +# +# void fpu_restore(struct fpu_state_struct *) +# - restore the fpu state +# - note that an FPU Operational exception might occur during this process +# +############################################################################### + .globl fpu_restore + .type fpu_restore,@function +fpu_restore: + mov epsw,d1 + or EPSW_FE,epsw /* enable the FPU so we can access it */ + +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop +#endif + mov d0,a0 + fmov (a0+),fs0 + fmov (a0+),fs1 + fmov (a0+),fs2 + fmov (a0+),fs3 + fmov (a0+),fs4 + fmov (a0+),fs5 + fmov (a0+),fs6 + fmov (a0+),fs7 + fmov (a0+),fs8 + fmov (a0+),fs9 + fmov (a0+),fs10 + fmov (a0+),fs11 + fmov (a0+),fs12 + fmov (a0+),fs13 + fmov (a0+),fs14 + fmov (a0+),fs15 + fmov (a0+),fs16 + fmov (a0+),fs17 + fmov (a0+),fs18 + fmov (a0+),fs19 + fmov (a0+),fs20 + fmov (a0+),fs21 + fmov (a0+),fs22 + fmov (a0+),fs23 + fmov (a0+),fs24 + fmov (a0+),fs25 + fmov (a0+),fs26 + fmov (a0+),fs27 + fmov (a0+),fs28 + fmov (a0+),fs29 + fmov (a0+),fs30 + fmov (a0+),fs31 + mov (a0),d0 + fmov d0,fpcr +#ifdef CONFIG_MN10300_PROC_MN103E010 + nop + nop + nop +#endif + + mov d1,epsw + ret [],0 + + .size fpu_restore,.-fpu_restore diff --git a/arch/mn10300/kernel/fpu.c b/arch/mn10300/kernel/fpu.c new file mode 100644 index 00000000000..e705f25ad5f --- /dev/null +++ b/arch/mn10300/kernel/fpu.c @@ -0,0 +1,223 @@ +/* MN10300 FPU management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/uaccess.h> +#include <asm/fpu.h> +#include <asm/elf.h> +#include <asm/exceptions.h> + +struct task_struct *fpu_state_owner; + +/* + * handle an exception due to the FPU being disabled + */ +asmlinkage void fpu_disabled(struct pt_regs *regs, enum exception_code code) +{ + struct task_struct *tsk = current; + + if (!user_mode(regs)) + die_if_no_fixup("An FPU Disabled exception happened in" + " kernel space\n", + regs, code); + +#ifdef CONFIG_FPU + preempt_disable(); + + /* transfer the last process's FPU state to memory */ + if (fpu_state_owner) { + fpu_save(&fpu_state_owner->thread.fpu_state); + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + } + + /* the current process now owns the FPU state */ + fpu_state_owner = tsk; + regs->epsw |= EPSW_FE; + + /* load the FPU with the current process's FPU state or invent a new + * clean one if the process doesn't have one */ + if (is_using_fpu(tsk)) { + fpu_restore(&tsk->thread.fpu_state); + } else { + fpu_init_state(); + set_using_fpu(tsk); + } + + preempt_enable(); +#else + { + siginfo_t info; + + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *) tsk->thread.uregs->pc; + info.si_code = FPE_FLTINV; + + force_sig_info(SIGFPE, &info, tsk); + } +#endif /* CONFIG_FPU */ +} + +/* + * handle an FPU operational exception + * - there's a possibility that if the FPU is asynchronous, the signal might + * be meant for a process other than the current one + */ +asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code) +{ + struct task_struct *tsk = fpu_state_owner; + siginfo_t info; + + if (!user_mode(regs)) + die_if_no_fixup("An FPU Operation exception happened in" + " kernel space\n", + regs, code); + + if (!tsk) + die_if_no_fixup("An FPU Operation exception happened," + " but the FPU is not in use", + regs, code); + + info.si_signo = SIGFPE; + info.si_errno = 0; + info.si_addr = (void *) tsk->thread.uregs->pc; + info.si_code = FPE_FLTINV; + +#ifdef CONFIG_FPU + { + u32 fpcr; + + /* get FPCR (we need to enable the FPU whilst we do this) */ + asm volatile(" or %1,epsw \n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + " nop \n" + " nop \n" + " nop \n" +#endif + " fmov fpcr,%0 \n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + " nop \n" + " nop \n" + " nop \n" +#endif + " and %2,epsw \n" + : "=&d"(fpcr) + : "i"(EPSW_FE), "i"(~EPSW_FE) + ); + + if (fpcr & FPCR_EC_Z) + info.si_code = FPE_FLTDIV; + else if (fpcr & FPCR_EC_O) + info.si_code = FPE_FLTOVF; + else if (fpcr & FPCR_EC_U) + info.si_code = FPE_FLTUND; + else if (fpcr & FPCR_EC_I) + info.si_code = FPE_FLTRES; + } +#endif + + force_sig_info(SIGFPE, &info, tsk); +} + +/* + * save the FPU state to a signal context + */ +int fpu_setup_sigcontext(struct fpucontext *fpucontext) +{ +#ifdef CONFIG_FPU + struct task_struct *tsk = current; + + if (!is_using_fpu(tsk)) + return 0; + + /* transfer the current FPU state to memory and cause fpu_init() to be + * triggered by the next attempted FPU operation by the current + * process. + */ + preempt_disable(); + + if (fpu_state_owner == tsk) { + fpu_save(&tsk->thread.fpu_state); + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + fpu_state_owner = NULL; + } + + preempt_enable(); + + /* we no longer have a valid current FPU state */ + clear_using_fpu(tsk); + + /* transfer the saved FPU state onto the userspace stack */ + if (copy_to_user(fpucontext, + &tsk->thread.fpu_state, + min(sizeof(struct fpu_state_struct), + sizeof(struct fpucontext)))) + return -1; + + return 1; +#else + return 0; +#endif +} + +/* + * kill a process's FPU state during restoration after signal handling + */ +void fpu_kill_state(struct task_struct *tsk) +{ +#ifdef CONFIG_FPU + /* disown anything left in the FPU */ + preempt_disable(); + + if (fpu_state_owner == tsk) { + fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE; + fpu_state_owner = NULL; + } + + preempt_enable(); +#endif + /* we no longer have a valid current FPU state */ + clear_using_fpu(tsk); +} + +/* + * restore the FPU state from a signal context + */ +int fpu_restore_sigcontext(struct fpucontext *fpucontext) +{ + struct task_struct *tsk = current; + int ret; + + /* load up the old FPU state */ + ret = copy_from_user(&tsk->thread.fpu_state, + fpucontext, + min(sizeof(struct fpu_state_struct), + sizeof(struct fpucontext))); + if (!ret) + set_using_fpu(tsk); + + return ret; +} + +/* + * fill in the FPU structure for a core dump + */ +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg) +{ + struct task_struct *tsk = current; + int fpvalid; + + fpvalid = is_using_fpu(tsk); + if (fpvalid) { + unlazy_fpu(tsk); + memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg)); + } + + return fpvalid; +} diff --git a/arch/mn10300/kernel/gdb-cache.S b/arch/mn10300/kernel/gdb-cache.S new file mode 100644 index 00000000000..1108badc3d3 --- /dev/null +++ b/arch/mn10300/kernel/gdb-cache.S @@ -0,0 +1,105 @@ +############################################################################### +# +# MN10300 Low-level cache purging routines for gdbstub +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cache.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> +#include <asm/frame.inc> +#include <asm/serial-regs.h> + + .text + +############################################################################### +# +# GDB stub cache purge +# +############################################################################### + .type gdbstub_purge_cache,@function +ENTRY(gdbstub_purge_cache) + ####################################################################### + # read the addresses tagged in the cache's tag RAM and attempt to flush + # those addresses specifically + # - we rely on the hardware to filter out invalid tag entry addresses + mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries + +mn10300_dcache_flush_loop: + mov (a0),d0 + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 + or L1_CACHE_TAG_VALID,d0 # retain valid entries in the + # cache + mov d0,(a1) # conditional purge + +mn10300_dcache_flush_skip: + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_dcache_flush_loop + +;; # unconditionally flush and invalidate the dcache +;; mov DCACHE_PURGE(0,0),a1 # dcache purge request address +;; mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of +;; # entries +;; +;; gdbstub_purge_cache__dcache_loop: +;; mov (a1),d0 # unconditional purge +;; +;; add L1_CACHE_BYTES,a1 +;; add -1,d1 +;; bne gdbstub_purge_cache__dcache_loop + + ####################################################################### + # now invalidate the icache + mov CHCTR,a0 + movhu (a0),a1 + + mov epsw,d1 + and ~EPSW_IE,epsw + nop + nop + + # disable the icache + and ~CHCTR_ICEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # invalidate + or CHCTR_ICINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + mov CHCTR,a0 + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # and reenable it + movhu a1,(a0) + movhu (a0),d0 # read back to flush + # (SIGILLs all over without this) + + mov d1,epsw + + ret [],0 + + .size gdbstub_purge_cache,.-gdbstub_purge_cache diff --git a/arch/mn10300/kernel/gdb-io-serial-low.S b/arch/mn10300/kernel/gdb-io-serial-low.S new file mode 100644 index 00000000000..c68dcd05220 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-serial-low.S @@ -0,0 +1,90 @@ +############################################################################### +# +# 16550 serial Rx interrupt handler for gdbstub I/O +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cpu-regs.h> +#include <asm/thread_info.h> +#include <asm/frame.inc> +#include <asm/intctl-regs.h> +#include <asm/unit/serial.h> + + .text + +############################################################################### +# +# GDB stub serial receive interrupt entry point +# - intended to run at interrupt priority 0 +# +############################################################################### + .globl gdbstub_io_rx_handler + .type gdbstub_io_rx_handler,@function +gdbstub_io_rx_handler: + movm [d2,d3,a2,a3],(sp) + +#if 1 + movbu (GDBPORT_SERIAL_IIR),d2 +#endif + + mov (gdbstub_rx_inp),a3 +gdbstub_io_rx_more: + mov a3,a2 + add 2,a3 + and 0x00000fff,a3 + mov (gdbstub_rx_outp),d3 + cmp a3,d3 + beq gdbstub_io_rx_overflow + + movbu (GDBPORT_SERIAL_LSR),d3 + btst UART_LSR_DR,d3 + beq gdbstub_io_rx_done + movbu (GDBPORT_SERIAL_RX),d2 + movbu d3,(gdbstub_rx_buffer+1,a2) + movbu d2,(gdbstub_rx_buffer,a2) + mov a3,(gdbstub_rx_inp) + bra gdbstub_io_rx_more + +gdbstub_io_rx_done: + mov GxICR_DETECT,d2 + movbu d2,(XIRQxICR(GDBPORT_SERIAL_IRQ)) # ACK the interrupt + movhu (XIRQxICR(GDBPORT_SERIAL_IRQ)),d2 # flush + movm (sp),[d2,d3,a2,a3] + bset 0x01,(gdbstub_busy) + beq gdbstub_io_rx_enter + rti + +gdbstub_io_rx_overflow: + bset 0x01,(gdbstub_rx_overflow) + bra gdbstub_io_rx_done + +gdbstub_io_rx_enter: + or EPSW_IE|EPSW_IM_1,epsw + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + mov 0x280,d1 + + mov fp,d0 + call gdbstub_rx_irq[],0 # gdbstub_rx_irq(regs,excep) + + and ~EPSW_IE,epsw + bclr 0x01,(gdbstub_busy) + + .globl gdbstub_return +gdbstub_return: + RESTORE_ALL + + .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler diff --git a/arch/mn10300/kernel/gdb-io-serial.c b/arch/mn10300/kernel/gdb-io-serial.c new file mode 100644 index 00000000000..9a6d4e8ebe7 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-serial.c @@ -0,0 +1,155 @@ +/* 16550 serial driver for gdbstub I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/nmi.h> + +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/serial-regs.h> +#include <asm/unit/serial.h> + +/* + * initialise the GDB stub + */ +void gdbstub_io_init(void) +{ + u16 tmp; + + /* set up the serial port */ + GDBPORT_SERIAL_LCR = UART_LCR_WLEN8; /* 1N8 */ + GDBPORT_SERIAL_FCR = (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + + FLOWCTL_CLEAR(DTR); + FLOWCTL_SET(RTS); + + gdbstub_io_set_baud(115200); + + /* we want to get serial receive interrupts */ + XIRQxICR(GDBPORT_SERIAL_IRQ) = 0; + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + + IVAR0 = EXCEP_IRQ_LEVEL0; + set_intr_stub(EXCEP_IRQ_LEVEL0, gdbstub_io_rx_handler); + + XIRQxICR(GDBPORT_SERIAL_IRQ) &= ~GxICR_REQUEST; + XIRQxICR(GDBPORT_SERIAL_IRQ) = GxICR_ENABLE | GxICR_LEVEL_0; + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + + GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI; + + /* permit level 0 IRQs to take place */ + asm volatile( + " and %0,epsw \n" + " or %1,epsw \n" + : + : "i"(~EPSW_IM), "i"(EPSW_IE | EPSW_IM_1) + ); +} + +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_io_set_baud(unsigned baud) +{ + unsigned value; + u8 lcr; + + value = 18432000 / 16 / baud; + + lcr = GDBPORT_SERIAL_LCR; + GDBPORT_SERIAL_LCR |= UART_LCR_DLAB; + GDBPORT_SERIAL_DLL = value & 0xff; + GDBPORT_SERIAL_DLM = (value >> 8) & 0xff; + GDBPORT_SERIAL_LCR = lcr; +} + +/* + * wait for a character to come from the debugger + */ +int gdbstub_io_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + + try_again: + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; +#ifdef CONFIG_MN10300_WD_TIMER + watchdog_alert_counter = 0; +#endif /* CONFIG_MN10300_WD_TIMER */ + goto try_again; + } + + ch = gdbstub_rx_buffer[ix++]; + st = gdbstub_rx_buffer[ix++]; + gdbstub_rx_outp = ix & 0x00000fff; + + if (st & UART_LSR_BI) { + gdbstub_proto("### GDB Rx Break Detected ###\n"); + return -EINTR; + } else if (st & (UART_LSR_FE | UART_LSR_OE | UART_LSR_PE)) { + gdbstub_proto("### GDB Rx Error (st=%02x) ###\n", st); + return -EIO; + } else { + gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n", ch, st); + *_ch = ch & 0x7f; + return 0; + } +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_char(unsigned char ch) +{ + FLOWCTL_SET(DTR); + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + + if (ch == 0x0a) { + GDBPORT_SERIAL_TX = 0x0d; + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + } + GDBPORT_SERIAL_TX = ch; + + FLOWCTL_CLEAR(DTR); +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_flush(void) +{ + LSR_WAIT_FOR(TEMT); + LSR_WAIT_FOR(THRE); + FLOWCTL_CLEAR(DTR); +} diff --git a/arch/mn10300/kernel/gdb-io-ttysm-low.S b/arch/mn10300/kernel/gdb-io-ttysm-low.S new file mode 100644 index 00000000000..677c7876307 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-ttysm-low.S @@ -0,0 +1,93 @@ +############################################################################### +# +# MN10300 On-chip serial Rx interrupt handler for GDB stub I/O +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> +#include <asm/frame.inc> +#include <asm/intctl-regs.h> +#include <asm/unit/serial.h> +#include "mn10300-serial.h" + + .text + +############################################################################### +# +# GDB stub serial receive interrupt entry point +# - intended to run at interrupt priority 0 +# +############################################################################### + .globl gdbstub_io_rx_handler + .type gdbstub_io_rx_handler,@function +gdbstub_io_rx_handler: + movm [d2,d3,a2,a3],(sp) + + mov (gdbstub_rx_inp),a3 +gdbstub_io_rx_more: + mov a3,a2 + add 2,a3 + and PAGE_SIZE_asm-1,a3 + mov (gdbstub_rx_outp),d3 + cmp a3,d3 + beq gdbstub_io_rx_overflow + + movbu (SCgSTR),d3 + btst SC01STR_RBF,d3 + beq gdbstub_io_rx_done + movbu (SCgRXB),d2 + movbu d3,(gdbstub_rx_buffer+1,a2) + movbu d2,(gdbstub_rx_buffer,a2) + mov a3,(gdbstub_rx_inp) + bra gdbstub_io_rx_more + +gdbstub_io_rx_done: + mov GxICR_DETECT,d2 + movbu d2,(GxICR(SCgRXIRQ)) # ACK the interrupt + movhu (GxICR(SCgRXIRQ)),d2 # flush + + movm (sp),[d2,d3,a2,a3] + bset 0x01,(gdbstub_busy) + beq gdbstub_io_rx_enter + rti + +gdbstub_io_rx_overflow: + bset 0x01,(gdbstub_rx_overflow) + bra gdbstub_io_rx_done + +############################################################################### +# +# debugging interrupt - enter the GDB stub proper +# +############################################################################### +gdbstub_io_rx_enter: + or EPSW_IE|EPSW_IM_1,epsw + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + mov 0x280,d1 + + mov fp,d0 + call gdbstub_rx_irq[],0 # gdbstub_io_rx_irq(regs,excep) + + and ~EPSW_IE,epsw + bclr 0x01,(gdbstub_busy) + + .globl gdbstub_return +gdbstub_return: + RESTORE_ALL + + .size gdbstub_io_rx_handler,.-gdbstub_io_rx_handler diff --git a/arch/mn10300/kernel/gdb-io-ttysm.c b/arch/mn10300/kernel/gdb-io-ttysm.c new file mode 100644 index 00000000000..c5451592d40 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-ttysm.c @@ -0,0 +1,299 @@ +/* MN10300 On-chip serial driver for gdbstub I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/tty.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/unit/clock.h> +#include "mn10300-serial.h" + +#if defined(CONFIG_GDBSTUB_ON_TTYSM0) +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif0; +#elif defined(CONFIG_GDBSTUB_ON_TTYSM1) +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif1; +#else +struct mn10300_serial_port *const gdbstub_port = &mn10300_serial_port_sif2; +#endif + + +/* + * initialise the GDB stub I/O routines + */ +void __init gdbstub_io_init(void) +{ + uint16_t scxctr; + int tmp; + + switch (gdbstub_port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + gdbstub_port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + gdbstub_port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + /* set up the serial port */ + gdbstub_io_set_baud(115200); + + /* we want to get serial receive interrupts */ + set_intr_level(gdbstub_port->rx_irq, GxICR_LEVEL_0); + set_intr_level(gdbstub_port->tx_irq, GxICR_LEVEL_0); + set_intr_stub(EXCEP_IRQ_LEVEL0, gdbstub_io_rx_handler); + + *gdbstub_port->rx_icr |= GxICR_ENABLE; + tmp = *gdbstub_port->rx_icr; + + /* enable the device */ + scxctr = SC01CTR_CLN_8BIT; /* 1N8 */ + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + scxctr |= SC0CTR_CK_TM8UFLOW_8; /* == SC1CTR_CK_TM9UFLOW_8 + == SC2CTR_CK_TM10UFLOW_8 */ + break; + + case MNSCx_DIV_TIMER_8BIT: + scxctr |= SC0CTR_CK_TM2UFLOW_8; + break; + } + + scxctr |= SC01CTR_TXE | SC01CTR_RXE; + + *gdbstub_port->_control = scxctr; + tmp = *gdbstub_port->_control; + + /* permit level 0 IRQs only */ + asm volatile( + " and %0,epsw \n" + " or %1,epsw \n" + : + : "i"(~EPSW_IM), "i"(EPSW_IE|EPSW_IM_1) + ); +} + +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_io_set_baud(unsigned baud) +{ + const unsigned bits = 10; /* 1 [start] + 8 [data] + 0 [parity] + + * 1 [stop] */ + unsigned long ioclk = gdbstub_port->ioclk; + unsigned xdiv, tmp; + uint16_t tmxbr; + uint8_t tmxmd; + + if (!baud) { + baud = 9600; + } else if (baud == 134) { + baud = 269; /* 134 is really 134.5 */ + xdiv = 2; + } + +try_alternative: + xdiv = 1; + + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + break; + + case MNSCx_DIV_TIMER_8BIT: + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + break; + } + + /* as a last resort, if the quotient is zero, default to 9600 bps */ + baud = 9600; + goto try_alternative; + +timer_okay: + gdbstub_port->uart.timeout = (2 * bits * HZ) / baud; + gdbstub_port->uart.timeout += HZ / 50; + + /* set the timer to produce the required baud rate */ + switch (gdbstub_port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *gdbstub_port->_tmxmd = 0; + *gdbstub_port->_tmxbr = tmxbr; + *gdbstub_port->_tmxmd = TM8MD_INIT_COUNTER; + *gdbstub_port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *gdbstub_port->_tmxmd = 0; + *(volatile u8 *) gdbstub_port->_tmxbr = (u8)tmxbr; + *gdbstub_port->_tmxmd = TM2MD_INIT_COUNTER; + *gdbstub_port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } +} + +/* + * wait for a character to come from the debugger + */ +int gdbstub_io_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + +try_again: + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; +#ifdef CONFIG_MN10300_WD_TIMER + watchdog_alert_counter = 0; +#endif /* CONFIG_MN10300_WD_TIMER */ + goto try_again; + } + + ch = gdbstub_rx_buffer[ix++]; + st = gdbstub_rx_buffer[ix++]; + gdbstub_rx_outp = ix & (PAGE_SIZE - 1); + + st &= SC01STR_RXF | SC01STR_RBF | SC01STR_FEF | SC01STR_PEF | + SC01STR_OEF; + + /* deal with what we've got + * - note that the UART doesn't do BREAK-detection for us + */ + if (st & SC01STR_FEF && ch == 0) { + switch (gdbstub_port->rx_brk) { + case 0: gdbstub_port->rx_brk = 1; goto try_again; + case 1: gdbstub_port->rx_brk = 2; goto try_again; + case 2: + gdbstub_port->rx_brk = 3; + gdbstub_proto("### GDB MNSERIAL Rx Break Detected" + " ###\n"); + return -EINTR; + default: + goto try_again; + } + } else if (st & SC01STR_FEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Framing Error ###\n"); + return -EIO; + } else if (st & SC01STR_OEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Overrun Error ###\n"); + return -EIO; + } else if (st & SC01STR_PEF) { + if (gdbstub_port->rx_brk) + goto try_again; + + gdbstub_proto("### GDB MNSERIAL Parity Error ###\n"); + return -EIO; + } else { + /* look for the tail-end char on a break run */ + if (gdbstub_port->rx_brk == 3) { + switch (ch) { + case 0xFF: + case 0xFE: + case 0xFC: + case 0xF8: + case 0xF0: + case 0xE0: + case 0xC0: + case 0x80: + case 0x00: + gdbstub_port->rx_brk = 0; + goto try_again; + default: + break; + } + } + + gdbstub_port->rx_brk = 0; + gdbstub_io("### GDB Rx %02x (st=%02x) ###\n", ch, st); + *_ch = ch & 0x7f; + return 0; + } +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_char(unsigned char ch) +{ + while (*gdbstub_port->_status & SC01STR_TBF) + continue; + + if (ch == 0x0a) { + *(u8 *) gdbstub_port->_txb = 0x0d; + while (*gdbstub_port->_status & SC01STR_TBF) + continue; + } + + *(u8 *) gdbstub_port->_txb = ch; +} + +/* + * flush the transmission buffers + */ +void gdbstub_io_tx_flush(void) +{ + while (*gdbstub_port->_status & (SC01STR_TBF | SC01STR_TXF)) + continue; +} diff --git a/arch/mn10300/kernel/gdb-low.S b/arch/mn10300/kernel/gdb-low.S new file mode 100644 index 00000000000..e2725552cd8 --- /dev/null +++ b/arch/mn10300/kernel/gdb-low.S @@ -0,0 +1,115 @@ +############################################################################### +# +# MN10300 Low-level gdbstub routines +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/cache.h> +#include <asm/cpu-regs.h> +#include <asm/exceptions.h> +#include <asm/frame.inc> +#include <asm/serial-regs.h> + + .text + +############################################################################### +# +# GDB stub read memory with guard +# - D0 holds the memory address to read +# - D1 holds the address to store the byte into +# +############################################################################### + .globl gdbstub_read_byte_guard + .globl gdbstub_read_byte_cont +ENTRY(gdbstub_read_byte) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_byte_guard: + movbu (a0),d1 +gdbstub_read_byte_cont: + movbu d1,(a1) + ret [],0 + + .globl gdbstub_read_word_guard + .globl gdbstub_read_word_cont +ENTRY(gdbstub_read_word) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_word_guard: + movhu (a0),d1 +gdbstub_read_word_cont: + movhu d1,(a1) + ret [],0 + + .globl gdbstub_read_dword_guard + .globl gdbstub_read_dword_cont +ENTRY(gdbstub_read_dword) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_read_dword_guard: + mov (a0),d1 +gdbstub_read_dword_cont: + mov d1,(a1) + ret [],0 + +############################################################################### +# +# GDB stub write memory with guard +# - D0 holds the byte to store +# - D1 holds the memory address to write +# +############################################################################### + .globl gdbstub_write_byte_guard + .globl gdbstub_write_byte_cont +ENTRY(gdbstub_write_byte) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_byte_guard: + movbu a0,(a1) +gdbstub_write_byte_cont: + ret [],0 + + .globl gdbstub_write_word_guard + .globl gdbstub_write_word_cont +ENTRY(gdbstub_write_word) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_word_guard: + movhu a0,(a1) +gdbstub_write_word_cont: + ret [],0 + + .globl gdbstub_write_dword_guard + .globl gdbstub_write_dword_cont +ENTRY(gdbstub_write_dword) + mov d0,a0 + mov d1,a1 + clr d0 +gdbstub_write_dword_guard: + mov a0,(a1) +gdbstub_write_dword_cont: + ret [],0 + +############################################################################### +# +# GDB stub BUG() trap +# +############################################################################### +ENTRY(__gdbstub_bug_trap) + .byte 0xF7,0xF7 # don't use 0xFF as the JTAG unit preempts that + ret [],0 diff --git a/arch/mn10300/kernel/gdb-stub.c b/arch/mn10300/kernel/gdb-stub.c new file mode 100644 index 00000000000..21891c71d54 --- /dev/null +++ b/arch/mn10300/kernel/gdb-stub.c @@ -0,0 +1,1947 @@ +/* MN10300 GDB stub + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified for Linux/mn10300 by David Howells <dhowells@redhat.com> + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + * + * ============== + * MORE EXAMPLES: + * ============== + * + * For reference -- the following are the steps that one + * company took (RidgeRun Inc) to get remote gdb debugging + * going. In this scenario the host machine was a PC and the + * target platform was a Galileo EVB64120A MIPS evaluation + * board. + * + * Step 1: + * First download gdb-5.0.tar.gz from the internet. + * and then build/install the package. + * + * Example: + * $ tar zxf gdb-5.0.tar.gz + * $ cd gdb-5.0 + * $ ./configure --target=am33_2.0-linux-gnu + * $ make + * $ install + * am33_2.0-linux-gnu-gdb + * + * Step 2: + * Configure linux for remote debugging and build it. + * + * Example: + * $ cd ~/linux + * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging> + * $ make dep; make vmlinux + * + * Step 3: + * Download the kernel to the remote target and start + * the kernel running. It will promptly halt and wait + * for the host gdb session to connect. It does this + * since the "Kernel Hacking" option has defined + * CONFIG_REMOTE_DEBUG which in turn enables your calls + * to: + * set_debug_traps(); + * breakpoint(); + * + * Step 4: + * Start the gdb session on the host. + * + * Example: + * $ am33_2.0-linux-gnu-gdb vmlinux + * (gdb) set remotebaud 115200 + * (gdb) target remote /dev/ttyS1 + * ...at this point you are connected to + * the remote target and can use gdb + * in the normal fasion. Setting + * breakpoints, single stepping, + * printing variables, etc. + * + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/bug.h> + +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/cacheflush.h> +#include <asm/serial-regs.h> +#include <asm/busctl-regs.h> +#include <asm/unit/leds.h> +#include <asm/unit/serial.h> + +/* define to use F7F7 rather than FF which is subverted by JTAG debugger */ +#undef GDBSTUB_USE_F7F7_AS_BREAKPOINT + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static const char gdbstub_banner[] = + "Linux/MN10300 GDB Stub (c) RedHat 2007\n"; + +u8 gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +u32 gdbstub_rx_inp; +u32 gdbstub_rx_outp; +u8 gdbstub_busy; +u8 gdbstub_rx_overflow; +u8 gdbstub_rx_unget; + +static u8 gdbstub_flush_caches; +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static char trans_buffer[BUFMAX]; + +static const char hexchars[] = "0123456789abcdef"; + +struct gdbstub_bkpt { + u8 *addr; /* address of breakpoint */ + u8 len; /* size of breakpoint */ + u8 origbytes[7]; /* original bytes */ +}; + +static struct gdbstub_bkpt gdbstub_bkpts[256]; + +/* + * local prototypes + */ +static void getpacket(char *buffer); +static int putpacket(char *buffer); +static int computeSignal(enum exception_code excep); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(const void *mem, char *buf, int count, + int may_fault); +static const char *hex2mem(const char *buf, void *_mem, int count, + int may_fault); + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + return -1; +} + +#ifdef CONFIG_GDBSTUB_DEBUGGING + +void debug_to_serial(const char *p, int n) +{ + __debug_to_serial(p, n); + /* gdbstub_console_write(NULL, p, n); */ +} + +void gdbstub_printk(const char *fmt, ...) +{ + va_list args; + int len; + + /* Emit the output into the temporary buffer */ + va_start(args, fmt); + len = vsnprintf(trans_buffer, sizeof(trans_buffer), fmt, args); + va_end(args); + debug_to_serial(trans_buffer, len); +} + +#endif + +static inline char *gdbstub_strcpy(char *dst, const char *src) +{ + int loop = 0; + while ((dst[loop] = src[loop])) + loop++; + return dst; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + unsigned char ch; + int count, i, ret, error; + + for (;;) { + /* + * wait around for the start character, + * ignore all other characters + */ + do { + gdbstub_io_rx_char(&ch, 0); + } while (ch != '$'); + + checksum = 0; + xmitcsum = -1; + count = 0; + error = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + + if (ch == '#') + break; + checksum += ch; + buffer[count] = ch; + count++; + } + + if (error == -EIO) { + gdbstub_proto("### GDB Rx Error - Skipping packet" + " ###\n"); + gdbstub_proto("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); + continue; + } + + if (count >= BUFMAX || error) + continue; + + buffer[count] = 0; + + /* read the checksum */ + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum = hex(ch) << 4; + + ret = gdbstub_io_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum |= hex(ch); + + if (error) { + if (error == -EIO) + gdbstub_io("### GDB Rx Error -" + " Skipping packet\n"); + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); + continue; + } + + /* check the checksum */ + if (checksum != xmitcsum) { + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); /* failed checksum */ + continue; + } + + gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum); + gdbstub_io("### GDB Tx ACK\n"); + gdbstub_io_tx_char('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + gdbstub_io_tx_char(buffer[0]); + gdbstub_io_tx_char(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = 0; + while (buffer[count]) + count++; + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + + break; + } +} + +/* + * send the packet in buffer. + * - return 0 if successfully ACK'd + * - return 1 if abandoned due to new incoming packet + */ +static int putpacket(char *buffer) +{ + unsigned char checksum; + unsigned char ch; + int count; + + /* + * $<packet info>#<checksum>. + */ + gdbstub_proto("### GDB Tx $'%s'#?? ###\n", buffer); + + do { + gdbstub_io_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + gdbstub_io_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_io_tx_char('#'); + gdbstub_io_tx_char(hexchars[checksum >> 4]); + gdbstub_io_tx_char(hexchars[checksum & 0xf]); + + } while (gdbstub_io_rx_char(&ch, 0), + ch == '-' && (gdbstub_io("### GDB Rx NAK\n"), 0), + ch != '-' && ch != '+' && + (gdbstub_io("### GDB Rx ??? %02x\n", ch), 0), + ch != '+' && ch != '$'); + + if (ch == '+') { + gdbstub_io("### GDB Rx ACK\n"); + return 0; + } + + gdbstub_io("### GDB Tx Abandoned\n"); + gdbstub_rx_unget = ch; + return 1; +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars++; + + (*ptr)++; + } + + return (numChars); +} + +/* + * We single-step by setting breakpoints. When an exception + * is handled, we need to restore the instructions hoisted + * when the breakpoints were set. + * + * This is where we save the original instructions. + */ +static struct gdb_bp_save { + u8 *addr; + u8 opcode[2]; +} step_bp[2]; + +static const unsigned char gdbstub_insn_sizes[256] = +{ + /* 1 2 3 4 5 6 7 8 9 a b c d e f */ + 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ + 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ + 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ +}; + +static int __gdbstub_mark_bp(u8 *addr, int ix) +{ + if (addr < (u8 *) 0x70000000UL) + return 0; + /* 70000000-7fffffff: vmalloc area */ + if (addr < (u8 *) 0x80000000UL) + goto okay; + if (addr < (u8 *) 0x8c000000UL) + return 0; + /* 8c000000-93ffffff: SRAM, SDRAM */ + if (addr < (u8 *) 0x94000000UL) + goto okay; + return 0; + +okay: + if (gdbstub_read_byte(addr + 0, &step_bp[ix].opcode[0]) < 0 || + gdbstub_read_byte(addr + 1, &step_bp[ix].opcode[1]) < 0) + return 0; + + step_bp[ix].addr = addr; + return 1; +} + +static inline void __gdbstub_restore_bp(void) +{ +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (step_bp[0].addr) { + gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0); + gdbstub_write_byte(step_bp[0].opcode[1], step_bp[0].addr + 1); + } + if (step_bp[1].addr) { + gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0); + gdbstub_write_byte(step_bp[1].opcode[1], step_bp[1].addr + 1); + } +#else + if (step_bp[0].addr) + gdbstub_write_byte(step_bp[0].opcode[0], step_bp[0].addr + 0); + if (step_bp[1].addr) + gdbstub_write_byte(step_bp[1].opcode[0], step_bp[1].addr + 0); +#endif + + gdbstub_flush_caches = 1; + + step_bp[0].addr = NULL; + step_bp[0].opcode[0] = 0; + step_bp[0].opcode[1] = 0; + step_bp[1].addr = NULL; + step_bp[1].opcode[0] = 0; + step_bp[1].opcode[1] = 0; +} + +/* + * emulate single stepping by means of breakpoint instructions + */ +static int gdbstub_single_step(struct pt_regs *regs) +{ + unsigned size; + uint32_t x; + uint8_t cur, *pc, *sp; + + step_bp[0].addr = NULL; + step_bp[0].opcode[0] = 0; + step_bp[0].opcode[1] = 0; + step_bp[1].addr = NULL; + step_bp[1].opcode[0] = 0; + step_bp[1].opcode[1] = 0; + x = 0; + + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + if (gdbstub_read_byte(pc, &cur) < 0) + return -EFAULT; + + gdbstub_bkpt("Single Step from %p { %02x }\n", pc, cur); + + gdbstub_flush_caches = 1; + + size = gdbstub_insn_sizes[cur]; + if (size > 0) { + if (!__gdbstub_mark_bp(pc + size, 0)) + goto fault; + } else { + switch (cur) { + /* Bxx (d8,PC) */ + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + case 0xc6: + case 0xc7: + case 0xc8: + case 0xc9: + case 0xca: + if (gdbstub_read_byte(pc + 1, (u8 *) &x) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + 2, 0)) + goto fault; + if ((x < 0 || x > 2) && + !__gdbstub_mark_bp(pc + (s8) x, 1)) + goto fault; + break; + + /* LXX (d8,PC) */ + case 0xd0: + case 0xd1: + case 0xd2: + case 0xd3: + case 0xd4: + case 0xd5: + case 0xd6: + case 0xd7: + case 0xd8: + case 0xd9: + case 0xda: + if (!__gdbstub_mark_bp(pc + 1, 0)) + goto fault; + if (regs->pc != regs->lar && + !__gdbstub_mark_bp((u8 *) regs->lar, 1)) + goto fault; + break; + + /* SETLB - loads the next for bytes into the LIR + * register */ + case 0xdb: + if (!__gdbstub_mark_bp(pc + 1, 0)) + goto fault; + break; + + /* JMP (d16,PC) or CALL (d16,PC) */ + case 0xcc: + case 0xcd: + if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s16) x, 0)) + goto fault; + break; + + /* JMP (d32,PC) or CALL (d32,PC) */ + case 0xdc: + case 0xdd: + if (gdbstub_read_byte(pc + 1, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(pc + 2, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte(pc + 3, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte(pc + 4, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s32) x, 0)) + goto fault; + break; + + /* RETF */ + case 0xde: + if (!__gdbstub_mark_bp((u8 *) regs->mdr, 0)) + goto fault; + break; + + /* RET */ + case 0xdf: + if (gdbstub_read_byte(pc + 2, (u8 *) &x) < 0) + goto fault; + sp += (s8)x; + if (gdbstub_read_byte(sp + 0, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte(sp + 1, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte(sp + 2, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte(sp + 3, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + break; + + case 0xf0: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + + if (cur >= 0xf0 && cur <= 0xf7) { + /* JMP (An) / CALLS (An) */ + switch (cur & 3) { + case 0: x = regs->a0; break; + case 1: x = regs->a1; break; + case 2: x = regs->a2; break; + case 3: x = regs->a3; break; + } + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else if (cur == 0xfc) { + /* RETS */ + if (gdbstub_read_byte( + sp + 0, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + sp + 1, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + sp + 2, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + sp + 3, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else if (cur == 0xfd) { + /* RTI */ + if (gdbstub_read_byte( + sp + 4, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + sp + 5, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + sp + 6, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + sp + 7, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp((u8 *) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp(pc + 2, 0)) + goto fault; + } + + break; + + /* potential 3-byte conditional branches */ + case 0xf8: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + 3, 0)) + goto fault; + + if (cur >= 0xe8 && cur <= 0xeb) { + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0) + goto fault; + if ((x < 0 || x > 3) && + !__gdbstub_mark_bp(pc + (s8) x, 1)) + goto fault; + } + break; + + case 0xfa: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + + if (cur == 0xff) { + /* CALLS (d16,PC) */ + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + pc + 3, ((u8 *) &x) + 1) < 0) + goto fault; + if (!__gdbstub_mark_bp(pc + (s16) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp(pc + 4, 0)) + goto fault; + } + break; + + case 0xfc: + if (gdbstub_read_byte(pc + 1, &cur) < 0) + goto fault; + if (cur == 0xff) { + /* CALLS (d32,PC) */ + if (gdbstub_read_byte( + pc + 2, ((u8 *) &x) + 0) < 0 || + gdbstub_read_byte( + pc + 3, ((u8 *) &x) + 1) < 0 || + gdbstub_read_byte( + pc + 4, ((u8 *) &x) + 2) < 0 || + gdbstub_read_byte( + pc + 5, ((u8 *) &x) + 3) < 0) + goto fault; + if (!__gdbstub_mark_bp( + pc + (s32) x, 0)) + goto fault; + } else { + if (!__gdbstub_mark_bp( + pc + 6, 0)) + goto fault; + } + break; + + } + } + + gdbstub_bkpt("Step: %02x at %p; %02x at %p\n", + step_bp[0].opcode[0], step_bp[0].addr, + step_bp[1].opcode[0], step_bp[1].addr); + + if (step_bp[0].addr) { +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (gdbstub_write_byte(0xF7, step_bp[0].addr + 0) < 0 || + gdbstub_write_byte(0xF7, step_bp[0].addr + 1) < 0) + goto fault; +#else + if (gdbstub_write_byte(0xFF, step_bp[0].addr + 0) < 0) + goto fault; +#endif + } + + if (step_bp[1].addr) { +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + if (gdbstub_write_byte(0xF7, step_bp[1].addr + 0) < 0 || + gdbstub_write_byte(0xF7, step_bp[1].addr + 1) < 0) + goto fault; +#else + if (gdbstub_write_byte(0xFF, step_bp[1].addr + 0) < 0) + goto fault; +#endif + } + + return 0; + + fault: + /* uh-oh - silly address alert, try and restore things */ + __gdbstub_restore_bp(); + return -EFAULT; +} + +#ifdef CONFIG_GDBSTUB_CONSOLE + +void gdbstub_console_write(struct console *con, const char *p, unsigned n) +{ + static const char gdbstub_cr[] = { 0x0d }; + char outbuf[26]; + int qty; + u8 busy; + + busy = gdbstub_busy; + gdbstub_busy = 1; + + outbuf[0] = 'O'; + + while (n > 0) { + qty = 1; + + while (n > 0 && qty < 20) { + mem2hex(p, outbuf + qty, 2, 0); + qty += 2; + if (*p == 0x0a) { + mem2hex(gdbstub_cr, outbuf + qty, 2, 0); + qty += 2; + } + p++; + n--; + } + + outbuf[qty] = 0; + putpacket(outbuf); + } + + gdbstub_busy = busy; +} + +static kdev_t gdbstub_console_dev(struct console *con) +{ + return MKDEV(1, 3); /* /dev/null */ +} + +static struct console gdbstub_console = { + .name = "gdb", + .write = gdbstub_console_write, + .device = gdbstub_console_dev, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +#endif + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * - if successful, return a pointer to the last char put in buf (NUL) + * - in case of mem fault, return NULL + * may_fault is non-zero if we are reading from arbitrary memory, but is + * currently not used. + */ +static +unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) +{ + const u8 *mem = _mem; + u8 ch[4]; + + if ((u32) mem & 1 && count >= 1) { + if (gdbstub_read_byte(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + mem++; + count--; + } + + if ((u32) mem & 3 && count >= 2) { + if (gdbstub_read_word(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + *buf++ = hexchars[ch[1] >> 4]; + *buf++ = hexchars[ch[1] & 0xf]; + mem += 2; + count -= 2; + } + + while (count >= 4) { + if (gdbstub_read_dword(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + *buf++ = hexchars[ch[1] >> 4]; + *buf++ = hexchars[ch[1] & 0xf]; + *buf++ = hexchars[ch[2] >> 4]; + *buf++ = hexchars[ch[2] & 0xf]; + *buf++ = hexchars[ch[3] >> 4]; + *buf++ = hexchars[ch[3] & 0xf]; + mem += 4; + count -= 4; + } + + if (count >= 2) { + if (gdbstub_read_word(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + *buf++ = hexchars[ch[1] >> 4]; + *buf++ = hexchars[ch[1] & 0xf]; + mem += 2; + count -= 2; + } + + if (count >= 1) { + if (gdbstub_read_byte(mem, ch) != 0) + return 0; + *buf++ = hexchars[ch[0] >> 4]; + *buf++ = hexchars[ch[0] & 0xf]; + } + + *buf = 0; + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + * may_fault is non-zero if we are reading from arbitrary memory, but is + * currently not used. + */ +static +const char *hex2mem(const char *buf, void *_mem, int count, int may_fault) +{ + u8 *mem = _mem; + union { + u32 val; + u8 b[4]; + } ch; + + if ((u32) mem & 1 && count >= 1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (gdbstub_write_byte(ch.val, mem) != 0) + return 0; + mem++; + count--; + } + + if ((u32) mem & 3 && count >= 2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (gdbstub_write_word(ch.val, mem) != 0) + return 0; + mem += 2; + count -= 2; + } + + while (count >= 4) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + ch.b[2] = hex(*buf++) << 4; + ch.b[2] |= hex(*buf++); + ch.b[3] = hex(*buf++) << 4; + ch.b[3] |= hex(*buf++); + if (gdbstub_write_dword(ch.val, mem) != 0) + return 0; + mem += 4; + count -= 4; + } + + if (count >= 2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (gdbstub_write_word(ch.val, mem) != 0) + return 0; + mem += 2; + count -= 2; + } + + if (count >= 1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (gdbstub_write_byte(ch.val, mem) != 0) + return 0; + } + + return buf; +} + +/* + * This table contains the mapping between MN10300 exception codes, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static const struct excep_to_sig_map { + enum exception_code excep; /* MN10300 exception code */ + unsigned char signo; /* Signal that we map this into */ +} excep_to_sig_map[] = { + { EXCEP_ITLBMISS, SIGSEGV }, + { EXCEP_DTLBMISS, SIGSEGV }, + { EXCEP_TRAP, SIGTRAP }, + { EXCEP_ISTEP, SIGTRAP }, + { EXCEP_IBREAK, SIGTRAP }, + { EXCEP_OBREAK, SIGTRAP }, + { EXCEP_UNIMPINS, SIGILL }, + { EXCEP_UNIMPEXINS, SIGILL }, + { EXCEP_MEMERR, SIGSEGV }, + { EXCEP_MISALIGN, SIGSEGV }, + { EXCEP_BUSERROR, SIGBUS }, + { EXCEP_ILLINSACC, SIGSEGV }, + { EXCEP_ILLDATACC, SIGSEGV }, + { EXCEP_IOINSACC, SIGSEGV }, + { EXCEP_PRIVINSACC, SIGSEGV }, + { EXCEP_PRIVDATACC, SIGSEGV }, + { EXCEP_FPU_DISABLED, SIGFPE }, + { EXCEP_FPU_UNIMPINS, SIGFPE }, + { EXCEP_FPU_OPERATION, SIGFPE }, + { EXCEP_WDT, SIGALRM }, + { EXCEP_NMI, SIGQUIT }, + { EXCEP_IRQ_LEVEL0, SIGINT }, + { EXCEP_IRQ_LEVEL1, SIGINT }, + { EXCEP_IRQ_LEVEL2, SIGINT }, + { EXCEP_IRQ_LEVEL3, SIGINT }, + { EXCEP_IRQ_LEVEL4, SIGINT }, + { EXCEP_IRQ_LEVEL5, SIGINT }, + { EXCEP_IRQ_LEVEL6, SIGINT }, + { 0, 0} +}; + +/* + * convert the MN10300 exception code into a UNIX signal number + */ +static int computeSignal(enum exception_code excep) +{ + const struct excep_to_sig_map *map; + + for (map = excep_to_sig_map; map->signo; map++) + if (map->excep == excep) + return map->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +static u32 gdbstub_fpcr, gdbstub_fpufs_array[32]; + +/* + * + */ +static void gdbstub_store_fpu(void) +{ +#ifdef CONFIG_FPU + + asm volatile( + "or %2,epsw\n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + "nop\n" + "nop\n" +#endif + "mov %1, a1\n" + "fmov fs0, (a1+)\n" + "fmov fs1, (a1+)\n" + "fmov fs2, (a1+)\n" + "fmov fs3, (a1+)\n" + "fmov fs4, (a1+)\n" + "fmov fs5, (a1+)\n" + "fmov fs6, (a1+)\n" + "fmov fs7, (a1+)\n" + "fmov fs8, (a1+)\n" + "fmov fs9, (a1+)\n" + "fmov fs10, (a1+)\n" + "fmov fs11, (a1+)\n" + "fmov fs12, (a1+)\n" + "fmov fs13, (a1+)\n" + "fmov fs14, (a1+)\n" + "fmov fs15, (a1+)\n" + "fmov fs16, (a1+)\n" + "fmov fs17, (a1+)\n" + "fmov fs18, (a1+)\n" + "fmov fs19, (a1+)\n" + "fmov fs20, (a1+)\n" + "fmov fs21, (a1+)\n" + "fmov fs22, (a1+)\n" + "fmov fs23, (a1+)\n" + "fmov fs24, (a1+)\n" + "fmov fs25, (a1+)\n" + "fmov fs26, (a1+)\n" + "fmov fs27, (a1+)\n" + "fmov fs28, (a1+)\n" + "fmov fs29, (a1+)\n" + "fmov fs30, (a1+)\n" + "fmov fs31, (a1+)\n" + "fmov fpcr, %0\n" + : "=d"(gdbstub_fpcr) + : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE) + : "a1" + ); +#endif +} + +/* + * + */ +static void gdbstub_load_fpu(void) +{ +#ifdef CONFIG_FPU + + asm volatile( + "or %1,epsw\n" +#ifdef CONFIG_MN10300_PROC_MN103E010 + "nop\n" + "nop\n" +#endif + "mov %0, a1\n" + "fmov (a1+), fs0\n" + "fmov (a1+), fs1\n" + "fmov (a1+), fs2\n" + "fmov (a1+), fs3\n" + "fmov (a1+), fs4\n" + "fmov (a1+), fs5\n" + "fmov (a1+), fs6\n" + "fmov (a1+), fs7\n" + "fmov (a1+), fs8\n" + "fmov (a1+), fs9\n" + "fmov (a1+), fs10\n" + "fmov (a1+), fs11\n" + "fmov (a1+), fs12\n" + "fmov (a1+), fs13\n" + "fmov (a1+), fs14\n" + "fmov (a1+), fs15\n" + "fmov (a1+), fs16\n" + "fmov (a1+), fs17\n" + "fmov (a1+), fs18\n" + "fmov (a1+), fs19\n" + "fmov (a1+), fs20\n" + "fmov (a1+), fs21\n" + "fmov (a1+), fs22\n" + "fmov (a1+), fs23\n" + "fmov (a1+), fs24\n" + "fmov (a1+), fs25\n" + "fmov (a1+), fs26\n" + "fmov (a1+), fs27\n" + "fmov (a1+), fs28\n" + "fmov (a1+), fs29\n" + "fmov (a1+), fs30\n" + "fmov (a1+), fs31\n" + "fmov %2, fpcr\n" + : + : "g" (&gdbstub_fpufs_array), "i"(EPSW_FE), "d"(gdbstub_fpcr) + : "a1" + ); +#endif +} + +/* + * set a software breakpoint + */ +int gdbstub_set_breakpoint(u8 *addr, int len) +{ + int bkpt, loop, xloop; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + len = (len + 1) & ~1; +#endif + + gdbstub_bkpt("setbkpt(%p,%d)\n", addr, len); + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (!gdbstub_bkpts[bkpt].addr) + break; + if (bkpt < 0) + return -ENOSPC; + + for (loop = 0; loop < len; loop++) + if (gdbstub_read_byte(&addr[loop], + &gdbstub_bkpts[bkpt].origbytes[loop] + ) < 0) + return -EFAULT; + + gdbstub_flush_caches = 1; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(0xF7, &addr[loop]) < 0) + goto restore; +#else + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(0xFF, &addr[loop]) < 0) + goto restore; +#endif + + gdbstub_bkpts[bkpt].addr = addr; + gdbstub_bkpts[bkpt].len = len; + + gdbstub_bkpt("Set BKPT[%02x]: %p-%p {%02x%02x%02x%02x%02x%02x%02x}\n", + bkpt, + gdbstub_bkpts[bkpt].addr, + gdbstub_bkpts[bkpt].addr + gdbstub_bkpts[bkpt].len - 1, + gdbstub_bkpts[bkpt].origbytes[0], + gdbstub_bkpts[bkpt].origbytes[1], + gdbstub_bkpts[bkpt].origbytes[2], + gdbstub_bkpts[bkpt].origbytes[3], + gdbstub_bkpts[bkpt].origbytes[4], + gdbstub_bkpts[bkpt].origbytes[5], + gdbstub_bkpts[bkpt].origbytes[6] + ); + + return 0; + +restore: + for (xloop = 0; xloop < loop; xloop++) + gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[xloop], + addr + xloop); + return -EFAULT; +} + +/* + * clear a software breakpoint + */ +int gdbstub_clear_breakpoint(u8 *addr, int len) +{ + int bkpt, loop; + +#ifdef GDBSTUB_USE_F7F7_AS_BREAKPOINT + len = (len + 1) & ~1; +#endif + + gdbstub_bkpt("clearbkpt(%p,%d)\n", addr, len); + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (gdbstub_bkpts[bkpt].addr == addr && + gdbstub_bkpts[bkpt].len == len) + break; + if (bkpt < 0) + return -ENOENT; + + gdbstub_bkpts[bkpt].addr = NULL; + + gdbstub_flush_caches = 1; + + for (loop = 0; loop < len; loop++) + if (gdbstub_write_byte(gdbstub_bkpts[bkpt].origbytes[loop], + addr + loop) < 0) + return -EFAULT; + + return 0; +} + +/* + * This function does all command processing for interfacing to gdb + * - returns 1 if the exception should be skipped, 0 otherwise. + */ +static int gdbstub(struct pt_regs *regs, enum exception_code excep) +{ + unsigned long *stack; + unsigned long epsw, mdr; + uint32_t zero, ssp; + uint8_t broke; + char *ptr; + int sigval; + int addr; + int length; + int loop; + + if (excep == EXCEP_FPU_DISABLED) + return 0; + + gdbstub_flush_caches = 0; + + mn10300_set_gdbleds(1); + + asm volatile("mov mdr,%0" : "=d"(mdr)); + asm volatile("mov epsw,%0" : "=d"(epsw)); + asm volatile("mov %0,epsw" + :: "d"((epsw & ~EPSW_IM) | EPSW_IE | EPSW_IM_1)); + + gdbstub_store_fpu(); + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + /* skip the initial pause loop */ + if (regs->pc == (unsigned long) __gdbstub_pause) + regs->pc = (unsigned long) start_kernel; +#endif + + /* if we were single stepping, restore the opcodes hoisted for the + * breakpoint[s] */ + broke = 0; + if ((step_bp[0].addr && step_bp[0].addr == (u8 *) regs->pc) || + (step_bp[1].addr && step_bp[1].addr == (u8 *) regs->pc)) + broke = 1; + + __gdbstub_restore_bp(); + + if (gdbstub_rx_unget) { + sigval = SIGINT; + if (gdbstub_rx_unget != 3) + goto packet_waiting; + gdbstub_rx_unget = 0; + } + + stack = (unsigned long *) regs->sp; + sigval = broke ? SIGTRAP : computeSignal(excep); + + /* send information about a BUG() */ + if (!user_mode(regs) && excep == EXCEP_SYSCALL15) { + const struct bug_entry *bug; + + bug = find_bug(regs->pc); + if (bug) + goto found_bug; + length = snprintf(trans_buffer, sizeof(trans_buffer), + "BUG() at address %lx\n", regs->pc); + goto send_bug_pkt; + + found_bug: + length = snprintf(trans_buffer, sizeof(trans_buffer), + "BUG() at address %lx (%s:%d)\n", + regs->pc, bug->file, bug->line); + + send_bug_pkt: + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(trans_buffer, ptr, length, 0); + *ptr = 0; + putpacket(output_buffer); + + regs->pc -= 2; + sigval = SIGABRT; + } else if (regs->pc == (unsigned long) __gdbstub_bug_trap) { + regs->pc = regs->mdr; + sigval = SIGABRT; + } + + /* + * send a message to the debugger's user saying what happened if it may + * not be clear cut (we can't map exceptions onto signals properly) + */ + if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) { + static const char title[] = "Excep ", tbcberr[] = "BCBERR "; + static const char crlf[] = "\r\n"; + char hx; + u32 bcberr = BCBERR; + + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(title, ptr, sizeof(title) - 1, 0); + + hx = hexchars[(excep & 0xf000) >> 12]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(excep & 0x0f00) >> 8]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(excep & 0x00f0) >> 4]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(excep & 0x000f)]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + + ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); + *ptr = 0; + putpacket(output_buffer); /* send it off... */ + + /* BCBERR */ + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(tbcberr, ptr, sizeof(tbcberr) - 1, 0); + + hx = hexchars[(bcberr & 0xf0000000) >> 28]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x0f000000) >> 24]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x00f00000) >> 20]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x000f0000) >> 16]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x0000f000) >> 12]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x00000f00) >> 8]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x000000f0) >> 4]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + hx = hexchars[(bcberr & 0x0000000f)]; + *ptr++ = hexchars[hx >> 4]; *ptr++ = hexchars[hx & 0xf]; + + ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); + *ptr = 0; + putpacket(output_buffer); /* send it off... */ + } + + /* + * tell the debugger that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[GDB_REGID_PC >> 4]; + *ptr++ = hexchars[GDB_REGID_PC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(®s->pc, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[GDB_REGID_FP >> 4]; + *ptr++ = hexchars[GDB_REGID_FP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(®s->a3, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + ssp = (unsigned long) (regs + 1); + *ptr++ = hexchars[GDB_REGID_SP >> 4]; + *ptr++ = hexchars[GDB_REGID_SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(&ssp, ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ + +packet_waiting: + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) { + /* request repeat of last signal number */ + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hexchars[sigval >> 4]; + output_buffer[2] = hexchars[sigval & 0xf]; + output_buffer[3] = 0; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + zero = 0; + ssp = (u32) (regs + 1); + ptr = output_buffer; + ptr = mem2hex(®s->d0, ptr, 4, 0); + ptr = mem2hex(®s->d1, ptr, 4, 0); + ptr = mem2hex(®s->d2, ptr, 4, 0); + ptr = mem2hex(®s->d3, ptr, 4, 0); + ptr = mem2hex(®s->a0, ptr, 4, 0); + ptr = mem2hex(®s->a1, ptr, 4, 0); + ptr = mem2hex(®s->a2, ptr, 4, 0); + ptr = mem2hex(®s->a3, ptr, 4, 0); + + ptr = mem2hex(&ssp, ptr, 4, 0); /* 8 */ + ptr = mem2hex(®s->pc, ptr, 4, 0); + ptr = mem2hex(®s->mdr, ptr, 4, 0); + ptr = mem2hex(®s->epsw, ptr, 4, 0); + ptr = mem2hex(®s->lir, ptr, 4, 0); + ptr = mem2hex(®s->lar, ptr, 4, 0); + ptr = mem2hex(®s->mdrq, ptr, 4, 0); + + ptr = mem2hex(®s->e0, ptr, 4, 0); /* 15 */ + ptr = mem2hex(®s->e1, ptr, 4, 0); + ptr = mem2hex(®s->e2, ptr, 4, 0); + ptr = mem2hex(®s->e3, ptr, 4, 0); + ptr = mem2hex(®s->e4, ptr, 4, 0); + ptr = mem2hex(®s->e5, ptr, 4, 0); + ptr = mem2hex(®s->e6, ptr, 4, 0); + ptr = mem2hex(®s->e7, ptr, 4, 0); + + ptr = mem2hex(&ssp, ptr, 4, 0); + ptr = mem2hex(®s, ptr, 4, 0); + ptr = mem2hex(®s->sp, ptr, 4, 0); + ptr = mem2hex(®s->mcrh, ptr, 4, 0); /* 26 */ + ptr = mem2hex(®s->mcrl, ptr, 4, 0); + ptr = mem2hex(®s->mcvf, ptr, 4, 0); + + ptr = mem2hex(&gdbstub_fpcr, ptr, 4, 0); /* 29 - FPCR */ + ptr = mem2hex(&zero, ptr, 4, 0); + ptr = mem2hex(&zero, ptr, 4, 0); + for (loop = 0; loop < 32; loop++) + ptr = mem2hex(&gdbstub_fpufs_array[loop], + ptr, 4, 0); /* 32 - FS0-31 */ + + break; + + /* + * set the value of the CPU registers - return OK + */ + case 'G': + { + const char *ptr; + + ptr = &input_buffer[1]; + ptr = hex2mem(ptr, ®s->d0, 4, 0); + ptr = hex2mem(ptr, ®s->d1, 4, 0); + ptr = hex2mem(ptr, ®s->d2, 4, 0); + ptr = hex2mem(ptr, ®s->d3, 4, 0); + ptr = hex2mem(ptr, ®s->a0, 4, 0); + ptr = hex2mem(ptr, ®s->a1, 4, 0); + ptr = hex2mem(ptr, ®s->a2, 4, 0); + ptr = hex2mem(ptr, ®s->a3, 4, 0); + + ptr = hex2mem(ptr, &ssp, 4, 0); /* 8 */ + ptr = hex2mem(ptr, ®s->pc, 4, 0); + ptr = hex2mem(ptr, ®s->mdr, 4, 0); + ptr = hex2mem(ptr, ®s->epsw, 4, 0); + ptr = hex2mem(ptr, ®s->lir, 4, 0); + ptr = hex2mem(ptr, ®s->lar, 4, 0); + ptr = hex2mem(ptr, ®s->mdrq, 4, 0); + + ptr = hex2mem(ptr, ®s->e0, 4, 0); /* 15 */ + ptr = hex2mem(ptr, ®s->e1, 4, 0); + ptr = hex2mem(ptr, ®s->e2, 4, 0); + ptr = hex2mem(ptr, ®s->e3, 4, 0); + ptr = hex2mem(ptr, ®s->e4, 4, 0); + ptr = hex2mem(ptr, ®s->e5, 4, 0); + ptr = hex2mem(ptr, ®s->e6, 4, 0); + ptr = hex2mem(ptr, ®s->e7, 4, 0); + + ptr = hex2mem(ptr, &ssp, 4, 0); + ptr = hex2mem(ptr, &zero, 4, 0); + ptr = hex2mem(ptr, ®s->sp, 4, 0); + ptr = hex2mem(ptr, ®s->mcrh, 4, 0); /* 26 */ + ptr = hex2mem(ptr, ®s->mcrl, 4, 0); + ptr = hex2mem(ptr, ®s->mcvf, 4, 0); + + ptr = hex2mem(ptr, &zero, 4, 0); /* 29 - FPCR */ + ptr = hex2mem(ptr, &zero, 4, 0); + ptr = hex2mem(ptr, &zero, 4, 0); + for (loop = 0; loop < 32; loop++) /* 32 - FS0-31 */ + ptr = hex2mem(ptr, &zero, 4, 0); + +#if 0 + /* + * See if the stack pointer has moved. If so, then copy + * the saved locals and ins to the new location. + */ + unsigned long *newsp = (unsigned long *) registers[SP]; + if (sp != newsp) + sp = memcpy(newsp, sp, 16 * 4); +#endif + + gdbstub_strcpy(output_buffer, "OK"); + } + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) + ) { + if (mem2hex((char *) addr, output_buffer, + length, 1)) + break; + gdbstub_strcpy(output_buffer, "E03"); + } else { + gdbstub_strcpy(output_buffer, "E01"); + } + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA + * return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) && + *ptr++ == ':' + ) { + if (hex2mem(ptr, (char *) addr, length, 1)) + gdbstub_strcpy(output_buffer, "OK"); + else + gdbstub_strcpy(output_buffer, "E03"); + + gdbstub_flush_caches = 1; + } else { + gdbstub_strcpy(output_buffer, "E02"); + } + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no + * parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->pc = addr; + goto done; + + /* + * kill the program + */ + case 'k' : + goto done; /* just continue */ + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + /* + * Step to next instruction + */ + case 's': + /* + * using the T flag doesn't seem to perform single + * stepping (it seems to wind up being caught by the + * JTAG unit), so we have to use breakpoints and + * continue instead. + */ + if (gdbstub_single_step(regs) < 0) + /* ignore any fault error for now */ + gdbstub_printk("unable to set single-step" + " bp\n"); + goto done; + + /* + * Set baud rate (bBB) + */ + case 'b': + do { + int baudrate; + + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &baudrate)) { + gdbstub_strcpy(output_buffer, "B01"); + break; + } + + if (baudrate) { + /* ACK before changing speed */ + putpacket("OK"); + gdbstub_io_set_baud(baudrate); + } + } while (0); + break; + + /* + * Set breakpoint + */ + case 'Z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr, &loop) || *ptr++ != ',' || + !hexToInt(&ptr, &addr) || *ptr++ != ',' || + !hexToInt(&ptr, &length) + ) { + gdbstub_strcpy(output_buffer, "E01"); + break; + } + + /* only support software breakpoints */ + gdbstub_strcpy(output_buffer, "E03"); + if (loop != 0 || + length < 1 || + length > 7 || + (unsigned long) addr < 4096) + break; + + if (gdbstub_set_breakpoint((u8 *) addr, length) < 0) + break; + + gdbstub_strcpy(output_buffer, "OK"); + break; + + /* + * Clear breakpoint + */ + case 'z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr, &loop) || *ptr++ != ',' || + !hexToInt(&ptr, &addr) || *ptr++ != ',' || + !hexToInt(&ptr, &length) + ) { + gdbstub_strcpy(output_buffer, "E01"); + break; + } + + /* only support software breakpoints */ + gdbstub_strcpy(output_buffer, "E03"); + if (loop != 0 || + length < 1 || + length > 7 || + (unsigned long) addr < 4096) + break; + + if (gdbstub_clear_breakpoint((u8 *) addr, length) < 0) + break; + + gdbstub_strcpy(output_buffer, "OK"); + break; + + default: + gdbstub_proto("### GDB Unsupported Cmd '%s'\n", + input_buffer); + break; + } + + /* reply to the request */ + putpacket(output_buffer); + } + +done: + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + if (gdbstub_flush_caches) + gdbstub_purge_cache(); + + gdbstub_load_fpu(); + mn10300_set_gdbleds(0); + if (excep == EXCEP_NMI) + NMICR = NMICR_NMIF; + + touch_softlockup_watchdog(); + + local_irq_restore(epsw); + return 1; +} + +/* + * handle event interception + */ +asmlinkage int gdbstub_intercept(struct pt_regs *regs, + enum exception_code excep) +{ + static u8 notfirst = 1; + int ret; + + if (gdbstub_busy) + gdbstub_printk("--> gdbstub reentered itself\n"); + gdbstub_busy = 1; + + if (notfirst) { + unsigned long mdr; + asm("mov mdr,%0" : "=d"(mdr)); + + gdbstub_entry( + "--> gdbstub_intercept(%p,%04x) [MDR=%lx PC=%lx]\n", + regs, excep, mdr, regs->pc); + + gdbstub_entry( + "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n", + regs->pc, regs->epsw, (unsigned long) &ret, + user_mode(regs) ? "User" : "Super"); + gdbstub_entry( + "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + regs->d0, regs->d1, regs->d2, regs->d3); + gdbstub_entry( + "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n", + regs->a0, regs->a1, regs->a2, regs->a3); + gdbstub_entry( + "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n", + regs->e0, regs->e1, regs->e2, regs->e3); + gdbstub_entry( + "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n", + regs->e4, regs->e5, regs->e6, regs->e7); + gdbstub_entry( + "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n", + regs->lar, regs->lir, regs->mdr, regs->sp); + gdbstub_entry( + "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n", + regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq); + gdbstub_entry( + "threadinfo=%p task=%p)\n", + current_thread_info(), current); + } else { + notfirst = 1; + } + + ret = gdbstub(regs, excep); + + gdbstub_entry("<-- gdbstub_intercept()\n"); + gdbstub_busy = 0; + return ret; +} + +/* + * handle the GDB stub itself causing an exception + */ +asmlinkage void gdbstub_exception(struct pt_regs *regs, + enum exception_code excep) +{ + unsigned long mdr; + + asm("mov mdr,%0" : "=d"(mdr)); + gdbstub_entry("--> gdbstub exception({%p},%04x) [MDR=%lx]\n", + regs, excep, mdr); + + while ((unsigned long) regs == 0xffffffff) {} + + /* handle guarded memory accesses where we know it might fault */ + if (regs->pc == (unsigned) gdbstub_read_byte_guard) { + regs->pc = (unsigned) gdbstub_read_byte_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_read_word_guard) { + regs->pc = (unsigned) gdbstub_read_word_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_read_dword_guard) { + regs->pc = (unsigned) gdbstub_read_dword_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_byte_guard) { + regs->pc = (unsigned) gdbstub_write_byte_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_word_guard) { + regs->pc = (unsigned) gdbstub_write_word_cont; + goto fault; + } + + if (regs->pc == (unsigned) gdbstub_write_dword_guard) { + regs->pc = (unsigned) gdbstub_write_dword_cont; + goto fault; + } + + gdbstub_printk("\n### GDB stub caused an exception ###\n"); + + /* something went horribly wrong */ + console_verbose(); + show_registers(regs); + + panic("GDB Stub caused an unexpected exception - can't continue\n"); + + /* we caught an attempt by the stub to access silly memory */ +fault: + gdbstub_entry("<-- gdbstub exception() = EFAULT\n"); + regs->d0 = -EFAULT; + return; +} + +/* + * send an exit message to GDB + */ +void gdbstub_exit(int status) +{ + unsigned char checksum; + unsigned char ch; + int count; + + gdbstub_busy = 1; + output_buffer[0] = 'W'; + output_buffer[1] = hexchars[(status >> 4) & 0x0F]; + output_buffer[2] = hexchars[status & 0x0F]; + output_buffer[3] = 0; + + gdbstub_io_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = output_buffer[count]) != 0) { + gdbstub_io_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_io_tx_char('#'); + gdbstub_io_tx_char(hexchars[checksum >> 4]); + gdbstub_io_tx_char(hexchars[checksum & 0xf]); + + /* make sure the output is flushed, or else RedBoot might clobber it */ + gdbstub_io_tx_flush(); + + gdbstub_busy = 0; +} + +/* + * initialise the GDB stub + */ +asmlinkage void __init gdbstub_init(void) +{ +#ifdef CONFIG_GDBSTUB_IMMEDIATE + unsigned char ch; + int ret; +#endif + + gdbstub_busy = 1; + + printk(KERN_INFO "%s", gdbstub_banner); + + gdbstub_io_init(); + + gdbstub_entry("--> gdbstub_init\n"); + + /* try to talk to GDB (or anyone insane enough to want to type GDB + * protocol by hand) */ + gdbstub_io("### GDB Tx ACK\n"); + gdbstub_io_tx_char('+'); /* 'hello world' */ + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + gdbstub_printk("GDB Stub waiting for packet\n"); + + /* in case GDB is started before us, ACK any packets that are already + * sitting there (presumably "$?#xx") + */ + do { gdbstub_io_rx_char(&ch, 0); } while (ch != '$'); + do { gdbstub_io_rx_char(&ch, 0); } while (ch != '#'); + /* eat first csum byte */ + do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0); + /* eat second csum byte */ + do { ret = gdbstub_io_rx_char(&ch, 0); } while (ret != 0); + + gdbstub_io("### GDB Tx NAK\n"); + gdbstub_io_tx_char('-'); /* NAK it */ + +#else + printk("GDB Stub ready\n"); +#endif + + gdbstub_busy = 0; + gdbstub_entry("<-- gdbstub_init\n"); +} + +/* + * register the console at a more appropriate time + */ +#ifdef CONFIG_GDBSTUB_CONSOLE +static int __init gdbstub_postinit(void) +{ + printk(KERN_NOTICE "registering console\n"); + register_console(&gdbstub_console); + return 0; +} + +__initcall(gdbstub_postinit); +#endif + +/* + * handle character reception on GDB serial port + * - jump into the GDB stub if BREAK is detected on the serial line + */ +asmlinkage void gdbstub_rx_irq(struct pt_regs *regs, enum exception_code excep) +{ + char ch; + int ret; + + gdbstub_entry("--> gdbstub_rx_irq\n"); + + do { + ret = gdbstub_io_rx_char(&ch, 1); + if (ret != -EIO && ret != -EAGAIN) { + if (ret != -EINTR) + gdbstub_rx_unget = ch; + gdbstub(regs, excep); + } + } while (ret != -EAGAIN); + + gdbstub_entry("<-- gdbstub_rx_irq\n"); +} diff --git a/arch/mn10300/kernel/head.S b/arch/mn10300/kernel/head.S new file mode 100644 index 00000000000..606bd8c6758 --- /dev/null +++ b/arch/mn10300/kernel/head.S @@ -0,0 +1,255 @@ +/* Boot entry point for MN10300 kernel + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/threads.h> +#include <linux/linkage.h> +#include <linux/serial_reg.h> +#include <asm/thread_info.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/frame.inc> +#include <asm/param.h> +#include <asm/unit/serial.h> + + .section .text.head,"ax" + +############################################################################### +# +# bootloader entry point +# +############################################################################### + .globl _start + .type _start,@function +_start: + # save commandline pointer + mov d0,a3 + + # preload the PGD pointer register + mov swapper_pg_dir,d0 + mov d0,(PTBR) + + # turn on the TLBs + mov MMUCTR_IIV|MMUCTR_DIV,d0 + mov d0,(MMUCTR) + mov MMUCTR_ITE|MMUCTR_DTE|MMUCTR_CE,d0 + mov d0,(MMUCTR) + + # turn on AM33v2 exception handling mode and set the trap table base + movhu (CPUP),d0 + or CPUP_EXM_AM33V2,d0 + movhu d0,(CPUP) + mov CONFIG_INTERRUPT_VECTOR_BASE,d0 + mov d0,(TBR) + + # invalidate and enable both of the caches + mov CHCTR,a0 + clr d0 + movhu d0,(a0) # turn off first + mov CHCTR_ICINV|CHCTR_DCINV,d0 + movhu d0,(a0) + setlb + mov (a0),d0 + btst CHCTR_ICBUSY|CHCTR_DCBUSY,d0 # wait till not busy + lne + +#ifndef CONFIG_MN10300_CACHE_DISABLED +#ifdef CONFIG_MN10300_CACHE_WBACK +#ifndef CONFIG_MN10300_CACHE_WBACK_NOWRALLOC + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK,d0 +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRBACK|CHCTR_DCALMD,d0 +#endif /* CACHE_DISABLED */ +#else + mov CHCTR_ICEN|CHCTR_DCEN|CHCTR_DCWTMD_WRTHROUGH,d0 +#endif /* WBACK */ + movhu d0,(a0) # enable +#endif /* NOWRALLOC */ + + # turn on RTS on the debug serial port if applicable +#ifdef CONFIG_MN10300_UNIT_ASB2305 + bset UART_MCR_RTS,(ASB2305_DEBUG_MCR) +#endif + + # clear the BSS area + mov __bss_start,a0 + mov __bss_stop,a1 + clr d0 +bssclear: + cmp a1,a0 + bge bssclear_end + mov d0,(a0) + inc4 a0 + bra bssclear +bssclear_end: + + # retrieve the parameters (including command line) before we overwrite + # them + cmp 0xabadcafe,d1 + bne __no_parameters + +__copy_parameters: + mov redboot_command_line,a0 + mov a0,a1 + add COMMAND_LINE_SIZE,a1 +1: + movbu (a3),d0 + inc a3 + movbu d0,(a0) + inc a0 + cmp a1,a0 + blt 1b + + mov redboot_platform_name,a0 + mov a0,a1 + add COMMAND_LINE_SIZE,a1 + mov d2,a3 +1: + movbu (a3),d0 + inc a3 + movbu d0,(a0) + inc a0 + cmp a1,a0 + blt 1b + +__no_parameters: + + # set up the registers with recognisable rubbish in them + mov init_thread_union+THREAD_SIZE-12,sp + + mov 0xea01eaea,d0 + mov d0,(4,sp) # EPSW save area + mov 0xea02eaea,d0 + mov d0,(8,sp) # PC save area + + mov 0xeb0060ed,d0 + mov d0,mdr + mov 0xeb0061ed,d0 + mov d0,mdrq + mov 0xeb0062ed,d0 + mov d0,mcrh + mov 0xeb0063ed,d0 + mov d0,mcrl + mov 0xeb0064ed,d0 + mov d0,mcvf + mov 0xed0065ed,a3 + mov a3,usp + + mov 0xed00e0ed,e0 + mov 0xed00e1ed,e1 + mov 0xed00e2ed,e2 + mov 0xed00e3ed,e3 + mov 0xed00e4ed,e4 + mov 0xed00e5ed,e5 + mov 0xed00e6ed,e6 + mov 0xed00e7ed,e7 + + mov 0xed00d0ed,d0 + mov 0xed00d1ed,d1 + mov 0xed00d2ed,d2 + mov 0xed00d3ed,d3 + mov 0xed00a0ed,a0 + mov 0xed00a1ed,a1 + mov 0xed00a2ed,a2 + mov 0,a3 + + # set up the initial kernel stack + SAVE_ALL + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + # put different recognisable rubbish in the regs + mov 0xfb0060ed,d0 + mov d0,mdr + mov 0xfb0061ed,d0 + mov d0,mdrq + mov 0xfb0062ed,d0 + mov d0,mcrh + mov 0xfb0063ed,d0 + mov d0,mcrl + mov 0xfb0064ed,d0 + mov d0,mcvf + mov 0xfd0065ed,a0 + mov a0,usp + + mov 0xfd00e0ed,e0 + mov 0xfd00e1ed,e1 + mov 0xfd00e2ed,e2 + mov 0xfd00e3ed,e3 + mov 0xfd00e4ed,e4 + mov 0xfd00e5ed,e5 + mov 0xfd00e6ed,e6 + mov 0xfd00e7ed,e7 + + mov 0xfd00d0ed,d0 + mov 0xfd00d1ed,d1 + mov 0xfd00d2ed,d2 + mov 0xfd00d3ed,d3 + mov 0xfd00a0ed,a0 + mov 0xfd00a1ed,a1 + mov 0xfd00a2ed,a2 + + # we may be holding current in E2 +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + mov init_task,e2 +#endif + + # initialise the processor and the unit + call processor_init[],0 + call unit_init[],0 + +#ifdef CONFIG_GDBSTUB + call gdbstub_init[],0 + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + .globl __gdbstub_pause +__gdbstub_pause: + bra __gdbstub_pause +#endif +#endif + + jmp start_kernel + .size _start, _start-. +ENTRY(__head_end) + +/* + * This is initialized to disallow all access to the low 2G region + * - the high 2G region is managed directly by the MMU + * - range 0x70000000-0x7C000000 are initialised for use by VMALLOC + */ + .section .bss + .balign PAGE_SIZE +ENTRY(swapper_pg_dir) + .space PTRS_PER_PGD*4 + +/* + * The page tables are initialized to only 8MB here - the final page + * tables are set up later depending on memory size. + */ + + .balign PAGE_SIZE +ENTRY(empty_zero_page) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(empty_bad_page) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(empty_bad_pte_table) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(large_page_table) + .space PAGE_SIZE + + .balign PAGE_SIZE +ENTRY(kernel_vmalloc_ptes) + .space ((VMALLOC_END-VMALLOC_START)/PAGE_SIZE)*4 diff --git a/arch/mn10300/kernel/init_task.c b/arch/mn10300/kernel/init_task.c new file mode 100644 index 00000000000..39fe6882dd1 --- /dev/null +++ b/arch/mn10300/kernel/init_task.c @@ -0,0 +1,45 @@ +/* MN10300 Initial task definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); +EXPORT_SYMBOL(init_mm); + +/* + * Initial thread structure. + * + * We need to make sure that this is THREAD_SIZE aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); +EXPORT_SYMBOL(init_task); diff --git a/arch/mn10300/kernel/internal.h b/arch/mn10300/kernel/internal.h new file mode 100644 index 00000000000..eee2eee8626 --- /dev/null +++ b/arch/mn10300/kernel/internal.h @@ -0,0 +1,20 @@ +/* Internal definitions for the arch part of the core kernel + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * kthread.S + */ +extern int kernel_thread_helper(int); + +/* + * entry.S + */ +extern void ret_from_fork(struct task_struct *) __attribute__((noreturn)); diff --git a/arch/mn10300/kernel/io.c b/arch/mn10300/kernel/io.c new file mode 100644 index 00000000000..e96fdf6bb54 --- /dev/null +++ b/arch/mn10300/kernel/io.c @@ -0,0 +1,30 @@ +/* MN10300 Misaligned multibyte-word I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <asm/io.h> + +/* + * output data from a potentially misaligned buffer + */ +void __outsl(unsigned long addr, const void *buffer, int count) +{ + const unsigned char *buf = buffer; + unsigned long val; + + while (count--) { + memcpy(&val, buf, 4); + outl(val, addr); + buf += 4; + } +} +EXPORT_SYMBOL(__outsl); diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c new file mode 100644 index 00000000000..761c434a248 --- /dev/null +++ b/arch/mn10300/kernel/irq.c @@ -0,0 +1,235 @@ +/* MN10300 Arch-specific interrupt handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/seq_file.h> +#include <asm/setup.h> + +unsigned long __mn10300_irq_enabled_epsw = EPSW_IE | EPSW_IM_7; +EXPORT_SYMBOL(__mn10300_irq_enabled_epsw); + +atomic_t irq_err_count; + +/* + * MN10300 INTC controller operations + */ +static void mn10300_cpupic_disable(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_enable(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_ack(unsigned int irq) +{ + u16 tmp; + *(volatile u8 *) &GxICR(irq) = GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_mask(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL); + tmp = GxICR(irq); +} + +static void mn10300_cpupic_mask_ack(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_unmask(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; + tmp = GxICR(irq); +} + +static void mn10300_cpupic_end(unsigned int irq) +{ + u16 tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; + tmp = GxICR(irq); +} + +static struct irq_chip mn10300_cpu_pic = { + .name = "cpu", + .disable = mn10300_cpupic_disable, + .enable = mn10300_cpupic_enable, + .ack = mn10300_cpupic_ack, + .mask = mn10300_cpupic_mask, + .mask_ack = mn10300_cpupic_mask_ack, + .unmask = mn10300_cpupic_unmask, + .end = mn10300_cpupic_end, +}; + +/* + * 'what should we do if we get a hw irq event on an illegal vector'. + * each architecture has to answer this themselves. + */ +void ack_bad_irq(int irq) +{ + printk(KERN_WARNING "unexpected IRQ trap at vector %02x\n", irq); +} + +/* + * change the level at which an IRQ executes + * - must not be called whilst interrupts are being processed! + */ +void set_intr_level(int irq, u16 level) +{ + u16 tmp; + + if (in_interrupt()) + BUG(); + + tmp = GxICR(irq); + GxICR(irq) = (tmp & GxICR_ENABLE) | level; + tmp = GxICR(irq); +} + +/* + * mark an interrupt to be ACK'd after interrupt handlers have been run rather + * than before + * - see Documentation/mn10300/features.txt + */ +void set_intr_postackable(int irq) +{ + set_irq_handler(irq, handle_level_irq); +} + +/* + * initialise the interrupt system + */ +void __init init_IRQ(void) +{ + int irq; + + for (irq = 0; irq < NR_IRQS; irq++) + if (irq_desc[irq].chip == &no_irq_type) + set_irq_chip_and_handler(irq, &mn10300_cpu_pic, + handle_edge_irq); + unit_init_IRQ(); +} + +/* + * handle normal device IRQs + */ +asmlinkage void do_IRQ(void) +{ + unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw; + int irq; + + sp = current_stack_pointer(); + if (sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN) + BUG(); + + /* make sure local_irq_enable() doesn't muck up the interrupt priority + * setting in EPSW */ + old_irq_enabled_epsw = __mn10300_irq_enabled_epsw; + local_save_flags(epsw); + __mn10300_irq_enabled_epsw = EPSW_IE | (EPSW_IM & epsw); + irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL; + + __IRQ_STAT(smp_processor_id(), __irq_count)++; + + irq_enter(); + + for (;;) { + /* ask the interrupt controller for the next IRQ to process + * - the result we get depends on EPSW.IM + */ + irq = IAGR & IAGR_GN; + if (!irq) + break; + + local_irq_restore(irq_disabled_epsw); + + generic_handle_irq(irq >> 2); + + /* restore IRQ controls for IAGR access */ + local_irq_restore(epsw); + } + + __mn10300_irq_enabled_epsw = old_irq_enabled_epsw; + + irq_exit(); +} + +/* + * Display interrupt management information through /proc/interrupts + */ +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j, cpu; + struct irqaction *action; + unsigned long flags; + + switch (i) { + /* display column title bar naming CPUs */ + case 0: + seq_printf(p, " "); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "CPU%d ", j); + seq_putc(p, '\n'); + break; + + /* display information rows, one per active CPU */ + case 1 ... NR_IRQS - 1: + spin_lock_irqsave(&irq_desc[i].lock, flags); + + action = irq_desc[i].action; + if (action) { + seq_printf(p, "%3d: ", i); + for_each_present_cpu(cpu) + seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]); + seq_printf(p, " %14s.%u", irq_desc[i].chip->name, + (GxICR(i) & GxICR_LEVEL) >> + GxICR_LEVEL_SHIFT); + seq_printf(p, " %s", action->name); + + for (action = action->next; + action; + action = action->next) + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); + } + + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + break; + + /* polish off with NMI and error counters */ + case NR_IRQS: + seq_printf(p, "NMI: "); + for (j = 0; j < NR_CPUS; j++) + if (cpu_online(j)) + seq_printf(p, "%10u ", nmi_count(j)); + seq_putc(p, '\n'); + + seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); + break; + } + + return 0; +} diff --git a/arch/mn10300/kernel/kernel_execve.S b/arch/mn10300/kernel/kernel_execve.S new file mode 100644 index 00000000000..86039f10526 --- /dev/null +++ b/arch/mn10300/kernel/kernel_execve.S @@ -0,0 +1,37 @@ +/* MN10300 In-kernel program execution + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/linkage.h> +#include <asm/unistd.h> + +############################################################################### +# +# Do a system call from kernel instead of calling sys_execve so we end up with +# proper pt_regs. +# +# int kernel_execve(const char *filename, char *const argv[], +# char *const envp[]) +# +# On entry: D0/D1/8(SP): arguments to function +# On return: D0: syscall return. +# +############################################################################### + .globl kernel_execve + .type kernel_execve,@function +kernel_execve: + mov a3,a1 + mov d0,a0 + mov (12,sp),a3 + mov +__NR_execve,d0 + syscall 0 + mov a1,a3 + rets + + .size kernel_execve,.-kernel_execve diff --git a/arch/mn10300/kernel/kprobes.c b/arch/mn10300/kernel/kprobes.c new file mode 100644 index 00000000000..dacafab00eb --- /dev/null +++ b/arch/mn10300/kernel/kprobes.c @@ -0,0 +1,653 @@ +/* MN10300 Kernel probes implementation + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Licence as published by + * the Free Software Foundation; either version 2 of the Licence, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public Licence + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/kprobes.h> +#include <linux/ptrace.h> +#include <linux/spinlock.h> +#include <linux/preempt.h> +#include <linux/kdebug.h> +#include <asm/cacheflush.h> + +struct kretprobe_blackpoint kretprobe_blacklist[] = { { NULL, NULL } }; +const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist); + +/* kprobe_status settings */ +#define KPROBE_HIT_ACTIVE 0x00000001 +#define KPROBE_HIT_SS 0x00000002 + +static struct kprobe *current_kprobe; +static unsigned long current_kprobe_orig_pc; +static unsigned long current_kprobe_next_pc; +static int current_kprobe_ss_flags; +static unsigned long kprobe_status; +static kprobe_opcode_t current_kprobe_ss_buf[MAX_INSN_SIZE + 2]; +static unsigned long current_kprobe_bp_addr; + +DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; + + +/* singlestep flag bits */ +#define SINGLESTEP_BRANCH 1 +#define SINGLESTEP_PCREL 2 + +#define READ_BYTE(p, valp) \ + do { *(u8 *)(valp) = *(u8 *)(p); } while (0) + +#define READ_WORD16(p, valp) \ + do { \ + READ_BYTE((p), (valp)); \ + READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \ + } while (0) + +#define READ_WORD32(p, valp) \ + do { \ + READ_BYTE((p), (valp)); \ + READ_BYTE((u8 *)(p) + 1, (u8 *)(valp) + 1); \ + READ_BYTE((u8 *)(p) + 2, (u8 *)(valp) + 2); \ + READ_BYTE((u8 *)(p) + 3, (u8 *)(valp) + 3); \ + } while (0) + + +static const u8 mn10300_insn_sizes[256] = +{ + /* 1 2 3 4 5 6 7 8 9 a b c d e f */ + 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, /* 0 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 1 */ + 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, /* 2 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, /* 3 */ + 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, /* 4 */ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, /* 5 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 8 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* 9 */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* a */ + 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, /* b */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, /* c */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */ + 0, 2, 2, 2, 2, 2, 2, 4, 0, 3, 0, 4, 0, 6, 7, 1 /* f */ +}; + +#define LT (1 << 0) +#define GT (1 << 1) +#define GE (1 << 2) +#define LE (1 << 3) +#define CS (1 << 4) +#define HI (1 << 5) +#define CC (1 << 6) +#define LS (1 << 7) +#define EQ (1 << 8) +#define NE (1 << 9) +#define RA (1 << 10) +#define VC (1 << 11) +#define VS (1 << 12) +#define NC (1 << 13) +#define NS (1 << 14) + +static const u16 cond_table[] = { + /* V C N Z */ + /* 0 0 0 0 */ (NE | NC | CC | VC | GE | GT | HI), + /* 0 0 0 1 */ (EQ | NC | CC | VC | GE | LE | LS), + /* 0 0 1 0 */ (NE | NS | CC | VC | LT | LE | HI), + /* 0 0 1 1 */ (EQ | NS | CC | VC | LT | LE | LS), + /* 0 1 0 0 */ (NE | NC | CS | VC | GE | GT | LS), + /* 0 1 0 1 */ (EQ | NC | CS | VC | GE | LE | LS), + /* 0 1 1 0 */ (NE | NS | CS | VC | LT | LE | LS), + /* 0 1 1 1 */ (EQ | NS | CS | VC | LT | LE | LS), + /* 1 0 0 0 */ (NE | NC | CC | VS | LT | LE | HI), + /* 1 0 0 1 */ (EQ | NC | CC | VS | LT | LE | LS), + /* 1 0 1 0 */ (NE | NS | CC | VS | GE | GT | HI), + /* 1 0 1 1 */ (EQ | NS | CC | VS | GE | LE | LS), + /* 1 1 0 0 */ (NE | NC | CS | VS | LT | LE | LS), + /* 1 1 0 1 */ (EQ | NC | CS | VS | LT | LE | LS), + /* 1 1 1 0 */ (NE | NS | CS | VS | GE | GT | LS), + /* 1 1 1 1 */ (EQ | NS | CS | VS | GE | LE | LS), +}; + +/* + * Calculate what the PC will be after executing next instruction + */ +static unsigned find_nextpc(struct pt_regs *regs, int *flags) +{ + unsigned size; + s8 x8; + s16 x16; + s32 x32; + u8 opc, *pc, *sp, *next; + + next = 0; + *flags = SINGLESTEP_PCREL; + + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + opc = *pc; + + size = mn10300_insn_sizes[opc]; + if (size > 0) { + next = pc + size; + } else { + switch (opc) { + /* Bxx (d8,PC) */ + case 0xc0 ... 0xca: + x8 = 2; + if (cond_table[regs->epsw & 0xf] & (1 << (opc & 0xf))) + x8 = (s8)pc[1]; + next = pc + x8; + *flags |= SINGLESTEP_BRANCH; + break; + + /* JMP (d16,PC) or CALL (d16,PC) */ + case 0xcc: + case 0xcd: + READ_WORD16(pc + 1, &x16); + next = pc + x16; + *flags |= SINGLESTEP_BRANCH; + break; + + /* JMP (d32,PC) or CALL (d32,PC) */ + case 0xdc: + case 0xdd: + READ_WORD32(pc + 1, &x32); + next = pc + x32; + *flags |= SINGLESTEP_BRANCH; + break; + + /* RETF */ + case 0xde: + next = (u8 *)regs->mdr; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + break; + + /* RET */ + case 0xdf: + sp += pc[2]; + READ_WORD32(sp, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + break; + + case 0xf0: + next = pc + 2; + opc = pc[1]; + if (opc >= 0xf0 && opc <= 0xf7) { + /* JMP (An) / CALLS (An) */ + switch (opc & 3) { + case 0: + next = (u8 *)regs->a0; + break; + case 1: + next = (u8 *)regs->a1; + break; + case 2: + next = (u8 *)regs->a2; + break; + case 3: + next = (u8 *)regs->a3; + break; + } + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } else if (opc == 0xfc) { + /* RETS */ + READ_WORD32(sp, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } else if (opc == 0xfd) { + /* RTI */ + READ_WORD32(sp + 4, &x32); + next = (u8 *)x32; + *flags &= ~SINGLESTEP_PCREL; + *flags |= SINGLESTEP_BRANCH; + } + break; + + /* potential 3-byte conditional branches */ + case 0xf8: + next = pc + 3; + opc = pc[1]; + if (opc >= 0xe8 && opc <= 0xeb && + (cond_table[regs->epsw & 0xf] & + (1 << ((opc & 0xf) + 3))) + ) { + READ_BYTE(pc+2, &x8); + next = pc + x8; + *flags |= SINGLESTEP_BRANCH; + } + break; + + case 0xfa: + if (pc[1] == 0xff) { + /* CALLS (d16,PC) */ + READ_WORD16(pc + 2, &x16); + next = pc + x16; + } else + next = pc + 4; + *flags |= SINGLESTEP_BRANCH; + break; + + case 0xfc: + x32 = 6; + if (pc[1] == 0xff) { + /* CALLS (d32,PC) */ + READ_WORD32(pc + 2, &x32); + } + next = pc + x32; + *flags |= SINGLESTEP_BRANCH; + break; + /* LXX (d8,PC) */ + /* SETLB - loads the next four bytes into the LIR reg */ + case 0xd0 ... 0xda: + case 0xdb: + panic("Can't singlestep Lxx/SETLB\n"); + break; + } + } + return (unsigned)next; + +} + +/* + * set up out of place singlestep of some branching instructions + */ +static unsigned __kprobes singlestep_branch_setup(struct pt_regs *regs) +{ + u8 opc, *pc, *sp, *next; + + next = NULL; + pc = (u8 *) regs->pc; + sp = (u8 *) (regs + 1); + + switch (pc[0]) { + case 0xc0 ... 0xca: /* Bxx (d8,PC) */ + case 0xcc: /* JMP (d16,PC) */ + case 0xdc: /* JMP (d32,PC) */ + case 0xf8: /* Bxx (d8,PC) 3-byte version */ + /* don't really need to do anything except cause trap */ + next = pc; + break; + + case 0xcd: /* CALL (d16,PC) */ + pc[1] = 5; + pc[2] = 0; + next = pc + 5; + break; + + case 0xdd: /* CALL (d32,PC) */ + pc[1] = 7; + pc[2] = 0; + pc[3] = 0; + pc[4] = 0; + next = pc + 7; + break; + + case 0xde: /* RETF */ + next = pc + 3; + regs->mdr = (unsigned) next; + break; + + case 0xdf: /* RET */ + sp += pc[2]; + next = pc + 3; + *(unsigned *)sp = (unsigned) next; + break; + + case 0xf0: + next = pc + 2; + opc = pc[1]; + if (opc >= 0xf0 && opc <= 0xf3) { + /* CALLS (An) */ + /* use CALLS (d16,PC) to avoid mucking with An */ + pc[0] = 0xfa; + pc[1] = 0xff; + pc[2] = 4; + pc[3] = 0; + next = pc + 4; + } else if (opc >= 0xf4 && opc <= 0xf7) { + /* JMP (An) */ + next = pc; + } else if (opc == 0xfc) { + /* RETS */ + next = pc + 2; + *(unsigned *) sp = (unsigned) next; + } else if (opc == 0xfd) { + /* RTI */ + next = pc + 2; + *(unsigned *)(sp + 4) = (unsigned) next; + } + break; + + case 0xfa: /* CALLS (d16,PC) */ + pc[2] = 4; + pc[3] = 0; + next = pc + 4; + break; + + case 0xfc: /* CALLS (d32,PC) */ + pc[2] = 6; + pc[3] = 0; + pc[4] = 0; + pc[5] = 0; + next = pc + 6; + break; + + case 0xd0 ... 0xda: /* LXX (d8,PC) */ + case 0xdb: /* SETLB */ + panic("Can't singlestep Lxx/SETLB\n"); + } + + return (unsigned) next; +} + +int __kprobes arch_prepare_kprobe(struct kprobe *p) +{ + return 0; +} + +void __kprobes arch_copy_kprobe(struct kprobe *p) +{ + memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE); +} + +void __kprobes arch_arm_kprobe(struct kprobe *p) +{ + *p->addr = BREAKPOINT_INSTRUCTION; + flush_icache_range((unsigned long) p->addr, + (unsigned long) p->addr + sizeof(kprobe_opcode_t)); +} + +void __kprobes arch_disarm_kprobe(struct kprobe *p) +{ + mn10300_dcache_flush(); + mn10300_icache_inv(); +} + +void arch_remove_kprobe(struct kprobe *p) +{ +} + +static inline +void __kprobes disarm_kprobe(struct kprobe *p, struct pt_regs *regs) +{ + *p->addr = p->opcode; + regs->pc = (unsigned long) p->addr; + mn10300_dcache_flush(); + mn10300_icache_inv(); +} + +static inline +void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) +{ + unsigned long nextpc; + + current_kprobe_orig_pc = regs->pc; + memcpy(current_kprobe_ss_buf, &p->ainsn.insn[0], MAX_INSN_SIZE); + regs->pc = (unsigned long) current_kprobe_ss_buf; + + nextpc = find_nextpc(regs, ¤t_kprobe_ss_flags); + if (current_kprobe_ss_flags & SINGLESTEP_PCREL) + current_kprobe_next_pc = + current_kprobe_orig_pc + (nextpc - regs->pc); + else + current_kprobe_next_pc = nextpc; + + /* branching instructions need special handling */ + if (current_kprobe_ss_flags & SINGLESTEP_BRANCH) + nextpc = singlestep_branch_setup(regs); + + current_kprobe_bp_addr = nextpc; + + *(u8 *) nextpc = BREAKPOINT_INSTRUCTION; + mn10300_dcache_flush_range2((unsigned) current_kprobe_ss_buf, + sizeof(current_kprobe_ss_buf)); + mn10300_icache_inv(); +} + +static inline int __kprobes kprobe_handler(struct pt_regs *regs) +{ + struct kprobe *p; + int ret = 0; + unsigned int *addr = (unsigned int *) regs->pc; + + /* We're in an interrupt, but this is clear and BUG()-safe. */ + preempt_disable(); + + /* Check we're not actually recursing */ + if (kprobe_running()) { + /* We *are* holding lock here, so this is safe. + Disarm the probe we just hit, and ignore it. */ + p = get_kprobe(addr); + if (p) { + disarm_kprobe(p, regs); + ret = 1; + } else { + p = current_kprobe; + if (p->break_handler && p->break_handler(p, regs)) + goto ss_probe; + } + /* If it's not ours, can't be delete race, (we hold lock). */ + goto no_kprobe; + } + + p = get_kprobe(addr); + if (!p) { + if (*addr != BREAKPOINT_INSTRUCTION) { + /* The breakpoint instruction was removed right after + * we hit it. Another cpu has removed either a + * probepoint or a debugger breakpoint at this address. + * In either case, no further handling of this + * interrupt is appropriate. + */ + ret = 1; + } + /* Not one of ours: let kernel handle it */ + goto no_kprobe; + } + + kprobe_status = KPROBE_HIT_ACTIVE; + current_kprobe = p; + if (p->pre_handler(p, regs)) { + /* handler has already set things up, so skip ss setup */ + return 1; + } + +ss_probe: + prepare_singlestep(p, regs); + kprobe_status = KPROBE_HIT_SS; + return 1; + +no_kprobe: + preempt_enable_no_resched(); + return ret; +} + +/* + * Called after single-stepping. p->addr is the address of the + * instruction whose first byte has been replaced by the "breakpoint" + * instruction. To avoid the SMP problems that can occur when we + * temporarily put back the original opcode to single-step, we + * single-stepped a copy of the instruction. The address of this + * copy is p->ainsn.insn. + */ +static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) +{ + /* we may need to fixup regs/stack after singlestepping a call insn */ + if (current_kprobe_ss_flags & SINGLESTEP_BRANCH) { + regs->pc = current_kprobe_orig_pc; + switch (p->ainsn.insn[0]) { + case 0xcd: /* CALL (d16,PC) */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 5; + break; + case 0xdd: /* CALL (d32,PC) */ + /* fixup mdr and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 7; + break; + case 0xf0: + if (p->ainsn.insn[1] >= 0xf0 && + p->ainsn.insn[1] <= 0xf3) { + /* CALLS (An) */ + /* fixup MDR and return address on stack */ + regs->mdr = regs->pc + 2; + *(unsigned *) regs->sp = regs->mdr; + } + break; + + case 0xfa: /* CALLS (d16,PC) */ + /* fixup MDR and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 4; + break; + + case 0xfc: /* CALLS (d32,PC) */ + /* fixup MDR and return address on stack */ + *(unsigned *) regs->sp = regs->mdr = regs->pc + 6; + break; + } + } + + regs->pc = current_kprobe_next_pc; + current_kprobe_bp_addr = 0; +} + +static inline int __kprobes post_kprobe_handler(struct pt_regs *regs) +{ + if (!kprobe_running()) + return 0; + + if (current_kprobe->post_handler) + current_kprobe->post_handler(current_kprobe, regs, 0); + + resume_execution(current_kprobe, regs); + reset_current_kprobe(); + preempt_enable_no_resched(); + return 1; +} + +/* Interrupts disabled, kprobe_lock held. */ +static inline +int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) +{ + if (current_kprobe->fault_handler && + current_kprobe->fault_handler(current_kprobe, regs, trapnr)) + return 1; + + if (kprobe_status & KPROBE_HIT_SS) { + resume_execution(current_kprobe, regs); + reset_current_kprobe(); + preempt_enable_no_resched(); + } + return 0; +} + +/* + * Wrapper routine to for handling exceptions. + */ +int __kprobes kprobe_exceptions_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = data; + + switch (val) { + case DIE_BREAKPOINT: + if (current_kprobe_bp_addr != args->regs->pc) { + if (kprobe_handler(args->regs)) + return NOTIFY_STOP; + } else { + if (post_kprobe_handler(args->regs)) + return NOTIFY_STOP; + } + break; + case DIE_GPF: + if (kprobe_running() && + kprobe_fault_handler(args->regs, args->trapnr)) + return NOTIFY_STOP; + break; + default: + break; + } + return NOTIFY_DONE; +} + +/* Jprobes support. */ +static struct pt_regs jprobe_saved_regs; +static struct pt_regs *jprobe_saved_regs_location; +static kprobe_opcode_t jprobe_saved_stack[MAX_STACK_SIZE]; + +int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct jprobe *jp = container_of(p, struct jprobe, kp); + + jprobe_saved_regs_location = regs; + memcpy(&jprobe_saved_regs, regs, sizeof(struct pt_regs)); + + /* Save a whole stack frame, this gets arguments + * pushed onto the stack after using up all the + * arg registers. + */ + memcpy(&jprobe_saved_stack, regs + 1, sizeof(jprobe_saved_stack)); + + /* setup return addr to the jprobe handler routine */ + regs->pc = (unsigned long) jp->entry; + return 1; +} + +void __kprobes jprobe_return(void) +{ + void *orig_sp = jprobe_saved_regs_location + 1; + + preempt_enable_no_resched(); + asm volatile(" mov %0,sp\n" + ".globl jprobe_return_bp_addr\n" + "jprobe_return_bp_addr:\n\t" + " .byte 0xff\n" + : : "d" (orig_sp)); +} + +extern void jprobe_return_bp_addr(void); + +int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) +{ + u8 *addr = (u8 *) regs->pc; + + if (addr == (u8 *) jprobe_return_bp_addr) { + if (jprobe_saved_regs_location != regs) { + printk(KERN_ERR"JPROBE:" + " Current regs (%p) does not match saved regs" + " (%p).\n", + regs, jprobe_saved_regs_location); + BUG(); + } + + /* Restore old register state. + */ + memcpy(regs, &jprobe_saved_regs, sizeof(struct pt_regs)); + + memcpy(regs + 1, &jprobe_saved_stack, + sizeof(jprobe_saved_stack)); + return 1; + } + return 0; +} + +int __init arch_init_kprobes(void) +{ + return 0; +} diff --git a/arch/mn10300/kernel/kthread.S b/arch/mn10300/kernel/kthread.S new file mode 100644 index 00000000000..b5ae467ac5e --- /dev/null +++ b/arch/mn10300/kernel/kthread.S @@ -0,0 +1,31 @@ +/* MN10300 Kernel thread trampoline function + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + .text + +############################################################################### +# +# kernel_thread_helper - trampoline for kernel_thread() +# +# On entry: +# A2 = address of function to call +# D2 = function argument +# +############################################################################### + .globl kernel_thread_helper + .type kernel_thread_helper,@function +kernel_thread_helper: + mov do_exit,d1 + mov d1,(sp) + mov d1,mdr + mov d2,d0 + jmp (a2) + + .size kernel_thread_helper,.-kernel_thread_helper diff --git a/arch/mn10300/kernel/mn10300-debug.c b/arch/mn10300/kernel/mn10300-debug.c new file mode 100644 index 00000000000..bd8196478cb --- /dev/null +++ b/arch/mn10300/kernel/mn10300-debug.c @@ -0,0 +1,58 @@ +/* Debugging stuff for the MN10300-based processors + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <asm/serial-regs.h> + +#undef MN10300_CONSOLE_ON_SERIO + +/* + * write a string directly through one of the serial ports on-board the MN10300 + */ +#ifdef MN10300_CONSOLE_ON_SERIO +void debug_to_serial_mnser(const char *p, int n) +{ + char ch; + + for (; n > 0; n--) { + ch = *p++; + +#if MN10300_CONSOLE_ON_SERIO == 0 + while (SC0STR & (SC01STR_TBF)) continue; + SC0TXB = ch; + while (SC0STR & (SC01STR_TBF)) continue; + if (ch == 0x0a) { + SC0TXB = 0x0d; + while (SC0STR & (SC01STR_TBF)) continue; + } + +#elif MN10300_CONSOLE_ON_SERIO == 1 + while (SC1STR & (SC01STR_TBF)) continue; + SC1TXB = ch; + while (SC1STR & (SC01STR_TBF)) continue; + if (ch == 0x0a) { + SC1TXB = 0x0d; + while (SC1STR & (SC01STR_TBF)) continue; + } + +#elif MN10300_CONSOLE_ON_SERIO == 2 + while (SC2STR & (SC2STR_TBF)) continue; + SC2TXB = ch; + while (SC2STR & (SC2STR_TBF)) continue; + if (ch == 0x0a) { + SC2TXB = 0x0d; + while (SC2STR & (SC2STR_TBF)) continue; + } + +#endif + } +} +#endif + diff --git a/arch/mn10300/kernel/mn10300-serial-low.S b/arch/mn10300/kernel/mn10300-serial-low.S new file mode 100644 index 00000000000..ef3f4c1df2a --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial-low.S @@ -0,0 +1,191 @@ +############################################################################### +# +# Virtual DMA driver for MN10300 serial ports +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/page.h> +#include <asm/smp.h> +#include <asm/cpu-regs.h> +#include <asm/frame.inc> +#include <asm/timer-regs.h> +#include <asm/proc/cache.h> +#include <asm/unit/timex.h> +#include "mn10300-serial.h" + +#define SCxCTR 0x00 +#define SCxICR 0x04 +#define SCxTXB 0x08 +#define SCxRXB 0x09 +#define SCxSTR 0x0c +#define SCxTIM 0x0d + + .text + +############################################################################### +# +# serial port interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# +############################################################################### + .balign L1_CACHE_BYTES +ENTRY(mn10300_serial_vdma_interrupt) + or EPSW_IE,psw # permit overriding by + # debugging interrupts + movm [d2,d3,a2,a3,exreg0],(sp) + + movhu (IAGR),a2 # see if which interrupt is + # pending + and IAGR_GN,a2 + add a2,a2 + add mn10300_serial_int_tbl,a2 + + mov (a2+),a3 + mov (__iobase,a3),e2 + mov (a2),a2 + jmp (a2) + +############################################################################### +# +# serial port receive interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# - stores data/status byte pairs in the ring buffer +# - induces a scheduler tick timer interrupt when done, which we then subvert +# on entry: +# A3 struct mn10300_serial_port * +# E2 I/O port base +# +############################################################################### +ENTRY(mn10300_serial_vdma_rx_handler) + mov (__rx_icr,a3),e3 + mov GxICR_DETECT,d2 + movbu d2,(e3) # ACK the interrupt + movhu (e3),d2 # flush + + mov (__rx_inp,a3),d3 + mov d3,a2 + add 2,d3 + and MNSC_BUFFER_SIZE-1,d3 + mov (__rx_outp,a3),d2 + cmp d3,d2 + beq mnsc_vdma_rx_overflow + + mov (__rx_buffer,a3),d2 + add d2,a2 + movhu (SCxSTR,e2),d2 + movbu d2,(1,a2) + movbu (SCxRXB,e2),d2 + movbu d2,(a2) + mov d3,(__rx_inp,a3) + bset MNSCx_RX_AVAIL,(__intr_flags,a3) + +mnsc_vdma_rx_done: + mov (__tm_icr,a3),a2 + mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2 + movhu d2,(a2) # request a slow interrupt + movhu (a2),d2 # flush + + movm (sp),[d2,d3,a2,a3,exreg0] + rti + +mnsc_vdma_rx_overflow: + bset MNSCx_RX_OVERF,(__intr_flags,a3) + bra mnsc_vdma_rx_done + +############################################################################### +# +# serial port transmit interrupt virtual DMA entry point +# - intended to run at interrupt priority 1 (not affected by local_irq_disable) +# - retrieves data bytes from the ring buffer and passes them to the serial port +# - induces a scheduler tick timer interrupt when done, which we then subvert +# A3 struct mn10300_serial_port * +# E2 I/O port base +# +############################################################################### + .balign L1_CACHE_BYTES +ENTRY(mn10300_serial_vdma_tx_handler) + mov (__tx_icr,a3),e3 + mov GxICR_DETECT,d2 + movbu d2,(e3) # ACK the interrupt + movhu (e3),d2 # flush + + btst 0x01,(__tx_break,a3) # handle transmit break request + bne mnsc_vdma_tx_break + + movbu (SCxSTR,e2),d2 # don't try and transmit a char if the + # buffer is not empty + btst SC01STR_TBF,d2 # (may have tried to jumpstart) + bne mnsc_vdma_tx_noint + + movbu (__tx_xchar,a3),d2 # handle hi-pri XON/XOFF + or d2,d2 + bne mnsc_vdma_tx_xchar + + mov (__tx_info_buffer,a3),a2 # get the uart_info struct for Tx + mov (__xmit_tail,a2),d3 + mov (__xmit_head,a2),d2 + cmp d3,d2 + beq mnsc_vdma_tx_empty + + mov (__xmit_buffer,a2),d2 # get a char from the buffer and + # transmit it + movbu (d3,d2),d2 + movbu d2,(SCxTXB,e2) # Tx + + inc d3 # advance the buffer pointer + and __UART_XMIT_SIZE-1,d3 + mov (__xmit_head,a2),d2 + mov d3,(__xmit_tail,a2) + + sub d3,d2 # see if we've written everything + beq mnsc_vdma_tx_empty + + and __UART_XMIT_SIZE-1,d2 # see if we just made a hole + cmp __UART_XMIT_SIZE-2,d2 + beq mnsc_vdma_tx_made_hole + +mnsc_vdma_tx_done: + mov (__tm_icr,a3),a2 + mov GxICR_LEVEL_6|GxICR_ENABLE|GxICR_REQUEST|GxICR_DETECT,d2 + movhu d2,(a2) # request a slow interrupt + movhu (a2),d2 # flush + +mnsc_vdma_tx_noint: + movm (sp),[d2,d3,a2,a3,exreg0] + rti + +mnsc_vdma_tx_empty: + mov +(GxICR_LEVEL_1|GxICR_DETECT),d2 + movhu d2,(e3) # disable the interrupt + movhu (e3),d2 # flush + + bset MNSCx_TX_EMPTY,(__intr_flags,a3) + bra mnsc_vdma_tx_done + +mnsc_vdma_tx_break: + movhu (SCxCTR,e2),d2 # turn on break mode + or SC01CTR_BKE,d2 + movhu d2,(SCxCTR,e2) + mov +(GxICR_LEVEL_1|GxICR_DETECT),d2 + movhu d2,(e3) # disable transmit interrupts on this + # channel + movhu (e3),d2 # flush + bra mnsc_vdma_tx_noint + +mnsc_vdma_tx_xchar: + bclr 0xff,(__tx_xchar,a3) + movbu d2,(SCxTXB,e2) + bra mnsc_vdma_tx_done + +mnsc_vdma_tx_made_hole: + bset MNSCx_TX_SPACE,(__intr_flags,a3) + bra mnsc_vdma_tx_done diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c new file mode 100644 index 00000000000..b9c268c6b2f --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial.c @@ -0,0 +1,1480 @@ +/* MN10300 On-chip serial port UART driver + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +static const char serial_name[] = "MN10300 Serial driver"; +static const char serial_version[] = "mn10300_serial-1.0"; +static const char serial_revdate[] = "2007-11-06"; + +#if defined(CONFIG_MN10300_TTYSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/serial.h> +#include <linux/circ_buf.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/serial-regs.h> +#include <asm/unit/timex.h> +#include "mn10300-serial.h" + +static inline __attribute__((format(printf, 1, 2))) +void no_printk(const char *fmt, ...) +{ +} + +#define kenter(FMT, ...) \ + printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) +#define _enter(FMT, ...) \ + no_printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) +#define kdebug(FMT, ...) \ + printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) +#define _debug(FMT, ...) \ + no_printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) +#define kproto(FMT, ...) \ + printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) +#define _proto(FMT, ...) \ + no_printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) + +#define NR_UARTS 3 + +#ifdef CONFIG_MN10300_TTYSM_CONSOLE +static void mn10300_serial_console_write(struct console *co, + const char *s, unsigned count); +static int __init mn10300_serial_console_setup(struct console *co, + char *options); + +static struct uart_driver mn10300_serial_driver; +static struct console mn10300_serial_console = { + .name = "ttySM", + .write = mn10300_serial_console_write, + .device = uart_console_device, + .setup = mn10300_serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &mn10300_serial_driver, +}; +#endif + +static struct uart_driver mn10300_serial_driver = { + .owner = NULL, + .driver_name = "mn10300-serial", + .dev_name = "ttySM", + .major = TTY_MAJOR, + .minor = 128, + .nr = NR_UARTS, +#ifdef CONFIG_MN10300_TTYSM_CONSOLE + .cons = &mn10300_serial_console, +#endif +}; + +static unsigned int mn10300_serial_tx_empty(struct uart_port *); +static void mn10300_serial_set_mctrl(struct uart_port *, unsigned int mctrl); +static unsigned int mn10300_serial_get_mctrl(struct uart_port *); +static void mn10300_serial_stop_tx(struct uart_port *); +static void mn10300_serial_start_tx(struct uart_port *); +static void mn10300_serial_send_xchar(struct uart_port *, char ch); +static void mn10300_serial_stop_rx(struct uart_port *); +static void mn10300_serial_enable_ms(struct uart_port *); +static void mn10300_serial_break_ctl(struct uart_port *, int ctl); +static int mn10300_serial_startup(struct uart_port *); +static void mn10300_serial_shutdown(struct uart_port *); +static void mn10300_serial_set_termios(struct uart_port *, + struct ktermios *new, + struct ktermios *old); +static const char *mn10300_serial_type(struct uart_port *); +static void mn10300_serial_release_port(struct uart_port *); +static int mn10300_serial_request_port(struct uart_port *); +static void mn10300_serial_config_port(struct uart_port *, int); +static int mn10300_serial_verify_port(struct uart_port *, + struct serial_struct *); + +static const struct uart_ops mn10300_serial_ops = { + .tx_empty = mn10300_serial_tx_empty, + .set_mctrl = mn10300_serial_set_mctrl, + .get_mctrl = mn10300_serial_get_mctrl, + .stop_tx = mn10300_serial_stop_tx, + .start_tx = mn10300_serial_start_tx, + .send_xchar = mn10300_serial_send_xchar, + .stop_rx = mn10300_serial_stop_rx, + .enable_ms = mn10300_serial_enable_ms, + .break_ctl = mn10300_serial_break_ctl, + .startup = mn10300_serial_startup, + .shutdown = mn10300_serial_shutdown, + .set_termios = mn10300_serial_set_termios, + .type = mn10300_serial_type, + .release_port = mn10300_serial_release_port, + .request_port = mn10300_serial_request_port, + .config_port = mn10300_serial_config_port, + .verify_port = mn10300_serial_verify_port, +}; + +static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id); + +/* + * the first on-chip serial port: ttySM0 (aka SIF0) + */ +#ifdef CONFIG_MN10300_TTYSM0 +struct mn10300_serial_port mn10300_serial_port_sif0 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC0CTR, + .uart.mapbase = (unsigned long) &SC0CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 0, + .uart.type = PORT_MN10300, + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif0.uart.lock), + .name = "ttySM0", + ._iobase = &SC0CTR, + ._control = &SC0CTR, + ._status = (volatile u8 *) &SC0STR, + ._intr = &SC0ICR, + ._rxb = &SC0RXB, + ._txb = &SC0TXB, + .rx_name = "ttySM0/Rx", + .tx_name = "ttySM0/Tx", +#ifdef CONFIG_MN10300_TTYSM0_TIMER8 + .tm_name = "ttySM0/Timer8", + ._tmxmd = &TM8MD, + ._tmxbr = &TM8BR, + ._tmicr = &TM8ICR, + .tm_irq = TM8IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#else /* CONFIG_MN10300_TTYSM0_TIMER2 */ + .tm_name = "ttySM0/Timer2", + ._tmxmd = &TM2MD, + ._tmxbr = (volatile u16 *) &TM2BR, + ._tmicr = &TM2ICR, + .tm_irq = TM2IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#endif + .rx_irq = SC0RXIRQ, + .tx_irq = SC0TXIRQ, + .rx_icr = &GxICR(SC0RXIRQ), + .tx_icr = &GxICR(SC0TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, + .options = 0, +#ifdef CONFIG_GDBSTUB_ON_TTYSM0 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM0 */ + +/* + * the second on-chip serial port: ttySM1 (aka SIF1) + */ +#ifdef CONFIG_MN10300_TTYSM1 +struct mn10300_serial_port mn10300_serial_port_sif1 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC1CTR, + .uart.mapbase = (unsigned long) &SC1CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 1, + .uart.type = PORT_MN10300, + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif1.uart.lock), + .name = "ttySM1", + ._iobase = &SC1CTR, + ._control = &SC1CTR, + ._status = (volatile u8 *) &SC1STR, + ._intr = &SC1ICR, + ._rxb = &SC1RXB, + ._txb = &SC1TXB, + .rx_name = "ttySM1/Rx", + .tx_name = "ttySM1/Tx", +#ifdef CONFIG_MN10300_TTYSM1_TIMER9 + .tm_name = "ttySM1/Timer9", + ._tmxmd = &TM9MD, + ._tmxbr = &TM9BR, + ._tmicr = &TM9ICR, + .tm_irq = TM9IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, +#else /* CONFIG_MN10300_TTYSM1_TIMER3 */ + .tm_name = "ttySM1/Timer3", + ._tmxmd = &TM3MD, + ._tmxbr = (volatile u16 *) &TM3BR, + ._tmicr = &TM3ICR, + .tm_irq = TM3IRQ, + .div_timer = MNSCx_DIV_TIMER_8BIT, +#endif + .rx_irq = SC1RXIRQ, + .tx_irq = SC1TXIRQ, + .rx_icr = &GxICR(SC1RXIRQ), + .tx_icr = &GxICR(SC1TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, + .options = 0, +#ifdef CONFIG_GDBSTUB_ON_TTYSM1 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM1 */ + +/* + * the third on-chip serial port: ttySM2 (aka SIF2) + */ +#ifdef CONFIG_MN10300_TTYSM2 +struct mn10300_serial_port mn10300_serial_port_sif2 = { + .uart.ops = &mn10300_serial_ops, + .uart.membase = (void __iomem *) &SC2CTR, + .uart.mapbase = (unsigned long) &SC2CTR, + .uart.iotype = UPIO_MEM, + .uart.irq = 0, + .uart.uartclk = 0, /* MN10300_IOCLK, */ + .uart.fifosize = 1, + .uart.flags = UPF_BOOT_AUTOCONF, + .uart.line = 2, +#ifdef CONFIG_MN10300_TTYSM2_CTS + .uart.type = PORT_MN10300_CTS, +#else + .uart.type = PORT_MN10300, +#endif + .uart.lock = + __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif2.uart.lock), + .name = "ttySM2", + .rx_name = "ttySM2/Rx", + .tx_name = "ttySM2/Tx", + .tm_name = "ttySM2/Timer10", + ._iobase = &SC2CTR, + ._control = &SC2CTR, + ._status = &SC2STR, + ._intr = &SC2ICR, + ._rxb = &SC2RXB, + ._txb = &SC2TXB, + ._tmxmd = &TM10MD, + ._tmxbr = &TM10BR, + ._tmicr = &TM10ICR, + .tm_irq = TM10IRQ, + .div_timer = MNSCx_DIV_TIMER_16BIT, + .rx_irq = SC2RXIRQ, + .tx_irq = SC2TXIRQ, + .rx_icr = &GxICR(SC2RXIRQ), + .tx_icr = &GxICR(SC2TXIRQ), + .clock_src = MNSCx_CLOCK_SRC_IOCLK, +#ifdef CONFIG_MN10300_TTYSM2_CTS + .options = MNSCx_OPT_CTS, +#else + .options = 0, +#endif +#ifdef CONFIG_GDBSTUB_ON_TTYSM2 + .gdbstub = 1, +#endif +}; +#endif /* CONFIG_MN10300_TTYSM2 */ + + +/* + * list of available serial ports + */ +struct mn10300_serial_port *mn10300_serial_ports[NR_UARTS + 1] = { +#ifdef CONFIG_MN10300_TTYSM0 + [0] = &mn10300_serial_port_sif0, +#endif +#ifdef CONFIG_MN10300_TTYSM1 + [1] = &mn10300_serial_port_sif1, +#endif +#ifdef CONFIG_MN10300_TTYSM2 + [2] = &mn10300_serial_port_sif2, +#endif + [NR_UARTS] = NULL, +}; + + +/* + * we abuse the serial ports' baud timers' interrupt lines to get the ability + * to deliver interrupts to userspace as we use the ports' interrupt lines to + * do virtual DMA on account of the ports having no hardware FIFOs + * + * we can generate an interrupt manually in the assembly stubs by writing to + * the enable and detect bits in the interrupt control register, so all we need + * to do here is disable the interrupt line + * + * note that we can't just leave the line enabled as the baud rate timer *also* + * generates interrupts + */ +static void mn10300_serial_mask_ack(unsigned int irq) +{ + u16 tmp; + GxICR(irq) = GxICR_LEVEL_6; + tmp = GxICR(irq); /* flush write buffer */ +} + +static void mn10300_serial_nop(unsigned int irq) +{ +} + +static struct irq_chip mn10300_serial_pic = { + .name = "mnserial", + .ack = mn10300_serial_mask_ack, + .mask = mn10300_serial_mask_ack, + .mask_ack = mn10300_serial_mask_ack, + .unmask = mn10300_serial_nop, + .end = mn10300_serial_nop, +}; + + +/* + * serial virtual DMA interrupt jump table + */ +struct mn10300_serial_int mn10300_serial_int_tbl[NR_IRQS]; + +static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port) +{ + u16 x; + *port->tx_icr = GxICR_LEVEL_1 | GxICR_DETECT; + x = *port->tx_icr; +} + +static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port) +{ + u16 x; + *port->tx_icr = GxICR_LEVEL_1 | GxICR_ENABLE; + x = *port->tx_icr; +} + +static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port) +{ + u16 x; + *port->rx_icr = GxICR_LEVEL_1 | GxICR_DETECT; + x = *port->rx_icr; +} + +/* + * multi-bit equivalent of test_and_clear_bit() + */ +static int mask_test_and_clear(volatile u8 *ptr, u8 mask) +{ + u32 epsw; + asm volatile(" bclr %1,(%2) \n" + " mov epsw,%0 \n" + : "=d"(epsw) : "d"(mask), "a"(ptr)); + return !(epsw & EPSW_FLAG_Z); +} + +/* + * receive chars from the ring buffer for this serial port + * - must do break detection here (not done in the UART) + */ +static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port) +{ + struct uart_icount *icount = &port->uart.icount; + struct tty_struct *tty = port->uart.info->tty; + unsigned ix; + int count; + u8 st, ch, push, status, overrun; + + _enter("%s", port->name); + + push = 0; + + count = CIRC_CNT(port->rx_inp, port->rx_outp, MNSC_BUFFER_SIZE); + count = tty_buffer_request_room(tty, count); + if (count == 0) { + if (!tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + +try_again: + /* pull chars out of the hat */ + ix = port->rx_outp; + if (ix == port->rx_inp) { + if (push && !tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + + ch = port->rx_buffer[ix++]; + st = port->rx_buffer[ix++]; + smp_rmb(); + port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); + port->uart.icount.rx++; + + st &= SC01STR_FEF | SC01STR_PEF | SC01STR_OEF; + status = 0; + overrun = 0; + + /* the UART doesn't detect BREAK, so we have to do that ourselves + * - it starts as a framing error on a NUL character + * - then we count another two NUL characters before issuing TTY_BREAK + * - then we end on a normal char or one that has all the bottom bits + * zero and the top bits set + */ + switch (port->rx_brk) { + case 0: + /* not breaking at the moment */ + break; + + case 1: + if (st & SC01STR_FEF && ch == 0) { + port->rx_brk = 2; + goto try_again; + } + goto not_break; + + case 2: + if (st & SC01STR_FEF && ch == 0) { + port->rx_brk = 3; + _proto("Rx Break Detected"); + icount->brk++; + if (uart_handle_break(&port->uart)) + goto ignore_char; + status |= 1 << TTY_BREAK; + goto insert; + } + goto not_break; + + default: + if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)) + goto try_again; /* still breaking */ + + port->rx_brk = 0; /* end of the break */ + + switch (ch) { + case 0xFF: + case 0xFE: + case 0xFC: + case 0xF8: + case 0xF0: + case 0xE0: + case 0xC0: + case 0x80: + case 0x00: + /* discard char at probable break end */ + goto try_again; + } + break; + } + +process_errors: + /* handle framing error */ + if (st & SC01STR_FEF) { + if (ch == 0) { + /* framing error with NUL char is probably a BREAK */ + port->rx_brk = 1; + goto try_again; + } + + _proto("Rx Framing Error"); + icount->frame++; + status |= 1 << TTY_FRAME; + } + + /* handle parity error */ + if (st & SC01STR_PEF) { + _proto("Rx Parity Error"); + icount->parity++; + status = TTY_PARITY; + } + + /* handle normal char */ + if (status == 0) { + if (uart_handle_sysrq_char(&port->uart, ch)) + goto ignore_char; + status = (1 << TTY_NORMAL); + } + + /* handle overrun error */ + if (st & SC01STR_OEF) { + if (port->rx_brk) + goto try_again; + + _proto("Rx Overrun Error"); + icount->overrun++; + overrun = 1; + } + +insert: + status &= port->uart.read_status_mask; + + if (!overrun && !(status & port->uart.ignore_status_mask)) { + int flag; + + if (status & (1 << TTY_BREAK)) + flag = TTY_BREAK; + else if (status & (1 << TTY_PARITY)) + flag = TTY_PARITY; + else if (status & (1 << TTY_FRAME)) + flag = TTY_FRAME; + else + flag = TTY_NORMAL; + + tty_insert_flip_char(tty, ch, flag); + } + + /* overrun is special, since it's reported immediately, and doesn't + * affect the current character + */ + if (overrun) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + + count--; + if (count <= 0) { + if (!tty->low_latency) + tty_flip_buffer_push(tty); + return; + } + +ignore_char: + push = 1; + goto try_again; + +not_break: + port->rx_brk = 0; + goto process_errors; +} + +/* + * handle an interrupt from the serial transmission "virtual DMA" driver + * - note: the interrupt routine will disable its own interrupts when the Tx + * buffer is empty + */ +static void mn10300_serial_transmit_interrupt(struct mn10300_serial_port *port) +{ + _enter("%s", port->name); + + if (uart_tx_stopped(&port->uart) || + uart_circ_empty(&port->uart.info->xmit)) + mn10300_serial_dis_tx_intr(port); + + if (uart_circ_chars_pending(&port->uart.info->xmit) < WAKEUP_CHARS) + uart_write_wakeup(&port->uart); +} + +/* + * deal with a change in the status of the CTS line + */ +static void mn10300_serial_cts_changed(struct mn10300_serial_port *port, u8 st) +{ + u16 ctr; + + port->tx_cts = st; + port->uart.icount.cts++; + + /* flip the CTS state selector flag to interrupt when it changes + * back */ + ctr = *port->_control; + ctr ^= SC2CTR_TWS; + *port->_control = ctr; + + uart_handle_cts_change(&port->uart, st & SC2STR_CTS); + wake_up_interruptible(&port->uart.info->delta_msr_wait); +} + +/* + * handle a virtual interrupt generated by the lower level "virtual DMA" + * routines (irq is the baud timer interrupt) + */ +static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id) +{ + struct mn10300_serial_port *port = dev_id; + u8 st; + + spin_lock(&port->uart.lock); + + if (port->intr_flags) { + _debug("INT %s: %x", port->name, port->intr_flags); + + if (mask_test_and_clear(&port->intr_flags, MNSCx_RX_AVAIL)) + mn10300_serial_receive_interrupt(port); + + if (mask_test_and_clear(&port->intr_flags, + MNSCx_TX_SPACE | MNSCx_TX_EMPTY)) + mn10300_serial_transmit_interrupt(port); + } + + /* the only modem control line amongst the whole lot is CTS on + * serial port 2 */ + if (port->type == PORT_MN10300_CTS) { + st = *port->_status; + if ((port->tx_cts ^ st) & SC2STR_CTS) + mn10300_serial_cts_changed(port, st); + } + + spin_unlock(&port->uart.lock); + + return IRQ_HANDLED; +} + +/* + * return indication of whether the hardware transmit buffer is empty + */ +static unsigned int mn10300_serial_tx_empty(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + return (*port->_status & (SC01STR_TXF | SC01STR_TBF)) ? + 0 : TIOCSER_TEMT; +} + +/* + * set the modem control lines (we don't have any) + */ +static void mn10300_serial_set_mctrl(struct uart_port *_port, + unsigned int mctrl) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%x", port->name, mctrl); +} + +/* + * get the modem control line statuses + */ +static unsigned int mn10300_serial_get_mctrl(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + if (port->type == PORT_MN10300_CTS && !(*port->_status & SC2STR_CTS)) + return TIOCM_CAR | TIOCM_DSR; + + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; +} + +/* + * stop transmitting characters + */ +static void mn10300_serial_stop_tx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + /* disable the virtual DMA */ + mn10300_serial_dis_tx_intr(port); +} + +/* + * start transmitting characters + * - jump-start transmission if it has stalled + * - enable the serial Tx interrupt (used by the virtual DMA controller) + * - force an interrupt to happen if necessary + */ +static void mn10300_serial_start_tx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 x; + + _enter("%s{%lu}", + port->name, + CIRC_CNT(&port->uart.info->xmit.head, + &port->uart.info->xmit.tail, + UART_XMIT_SIZE)); + + /* kick the virtual DMA controller */ + x = *port->tx_icr; + x |= GxICR_ENABLE; + + if (*port->_status & SC01STR_TBF) + x &= ~(GxICR_REQUEST | GxICR_DETECT); + else + x |= GxICR_REQUEST | GxICR_DETECT; + + _debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx", + *port->_control, *port->_intr, *port->_status, + *port->_tmxmd, *port->_tmxbr, *port->tx_icr); + + *port->tx_icr = x; + x = *port->tx_icr; +} + +/* + * transmit a high-priority XON/XOFF character + */ +static void mn10300_serial_send_xchar(struct uart_port *_port, char ch) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%02x", port->name, ch); + + if (likely(port->gdbstub)) { + port->tx_xchar = ch; + if (ch) + mn10300_serial_en_tx_intr(port); + } +} + +/* + * stop receiving characters + * - called whilst the port is being closed + */ +static void mn10300_serial_stop_rx(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 ctr; + + _enter("%s", port->name); + + ctr = *port->_control; + ctr &= ~SC01CTR_RXE; + *port->_control = ctr; + + mn10300_serial_dis_rx_intr(port); +} + +/* + * enable modem status interrupts + */ +static void mn10300_serial_enable_ms(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + u16 ctr, cts; + + _enter("%s", port->name); + + if (port->type == PORT_MN10300_CTS) { + /* want to interrupt when CTS goes low if CTS is now high and + * vice versa + */ + port->tx_cts = *port->_status; + + cts = (port->tx_cts & SC2STR_CTS) ? + SC2CTR_TWE : SC2CTR_TWE | SC2CTR_TWS; + + ctr = *port->_control; + ctr &= ~SC2CTR_TWS; + ctr |= cts; + *port->_control = ctr; + + mn10300_serial_en_tx_intr(port); + } +} + +/* + * transmit or cease transmitting a break signal + */ +static void mn10300_serial_break_ctl(struct uart_port *_port, int ctl) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%d", port->name, ctl); + + if (ctl) { + /* tell the virtual DMA handler to assert BREAK */ + port->tx_break = 1; + mn10300_serial_en_tx_intr(port); + } else { + port->tx_break = 0; + *port->_control &= ~SC01CTR_BKE; + mn10300_serial_en_tx_intr(port); + } +} + +/* + * grab the interrupts and enable the port for reception + */ +static int mn10300_serial_startup(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + struct mn10300_serial_int *pint; + + _enter("%s{%d}", port->name, port->gdbstub); + + if (unlikely(port->gdbstub)) + return -EBUSY; + + /* allocate an Rx buffer for the virtual DMA handler */ + port->rx_buffer = kmalloc(MNSC_BUFFER_SIZE, GFP_KERNEL); + if (!port->rx_buffer) + return -ENOMEM; + + port->rx_inp = port->rx_outp = 0; + + /* finally, enable the device */ + *port->_intr = SC01ICR_TI; + *port->_control |= SC01CTR_TXE | SC01CTR_RXE; + + pint = &mn10300_serial_int_tbl[port->rx_irq]; + pint->port = port; + pint->vdma = mn10300_serial_vdma_rx_handler; + pint = &mn10300_serial_int_tbl[port->tx_irq]; + pint->port = port; + pint->vdma = mn10300_serial_vdma_tx_handler; + + set_intr_level(port->rx_irq, GxICR_LEVEL_1); + set_intr_level(port->tx_irq, GxICR_LEVEL_1); + set_irq_chip(port->tm_irq, &mn10300_serial_pic); + + if (request_irq(port->rx_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->rx_name, port) < 0) + goto error; + + if (request_irq(port->tx_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->tx_name, port) < 0) + goto error2; + + if (request_irq(port->tm_irq, mn10300_serial_interrupt, + IRQF_DISABLED, port->tm_name, port) < 0) + goto error3; + mn10300_serial_mask_ack(port->tm_irq); + + return 0; + +error3: + free_irq(port->tx_irq, port); +error2: + free_irq(port->rx_irq, port); +error: + kfree(port->rx_buffer); + port->rx_buffer = NULL; + return -EBUSY; +} + +/* + * shutdown the port and release interrupts + */ +static void mn10300_serial_shutdown(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + /* disable the serial port and its baud rate timer */ + port->tx_break = 0; + *port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); + *port->_tmxmd = 0; + + if (port->rx_buffer) { + void *buf = port->rx_buffer; + port->rx_buffer = NULL; + kfree(buf); + } + + /* disable all intrs */ + free_irq(port->tm_irq, port); + free_irq(port->rx_irq, port); + free_irq(port->tx_irq, port); + + *port->rx_icr = GxICR_LEVEL_1; + *port->tx_icr = GxICR_LEVEL_1; +} + +/* + * this routine is called to set the UART divisor registers to match the + * specified baud rate for a serial port. + */ +static void mn10300_serial_change_speed(struct mn10300_serial_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned long flags; + unsigned long ioclk = port->ioclk; + unsigned cflag; + int baud, bits, xdiv, tmp; + u16 tmxbr, scxctr; + u8 tmxmd, battempt; + u8 div_timer = port->div_timer; + + _enter("%s{%lu}", port->name, ioclk); + + /* byte size and parity */ + cflag = new->c_cflag; + switch (cflag & CSIZE) { + case CS7: scxctr = SC01CTR_CLN_7BIT; bits = 9; break; + case CS8: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; + default: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; + } + + if (cflag & CSTOPB) { + scxctr |= SC01CTR_STB_2BIT; + bits++; + } + + if (cflag & PARENB) { + bits++; + if (cflag & PARODD) + scxctr |= SC01CTR_PB_ODD; +#ifdef CMSPAR + else if (cflag & CMSPAR) + scxctr |= SC01CTR_PB_FIXED0; +#endif + else + scxctr |= SC01CTR_PB_EVEN; + } + + /* Determine divisor based on baud rate */ + battempt = 0; + + if (div_timer == MNSCx_DIV_TIMER_16BIT) + scxctr |= SC0CTR_CK_TM8UFLOW_8; /* ( == SC1CTR_CK_TM9UFLOW_8 + * == SC2CTR_CK_TM10UFLOW) */ + else if (div_timer == MNSCx_DIV_TIMER_8BIT) + scxctr |= SC0CTR_CK_TM2UFLOW_8; + +try_alternative: + baud = uart_get_baud_rate(&port->uart, new, old, 0, + port->ioclk / 8); + + _debug("ALT %d [baud %d]", battempt, baud); + + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + xdiv = 1; + if (baud == 134) { + baud = 269; /* 134 is really 134.5 */ + xdiv = 2; + } + + if (baud == 38400 && + (port->uart.flags & UPF_SPD_MASK) == UPF_SPD_CUST + ) { + _debug("CUSTOM %u", port->uart.custom_divisor); + + if (div_timer == MNSCx_DIV_TIMER_16BIT) { + if (port->uart.custom_divisor <= 65535) { + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = port->uart.custom_divisor; + port->uart.uartclk = ioclk; + goto timer_okay; + } + if (port->uart.custom_divisor / 8 <= 65535) { + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = port->uart.custom_divisor / 8; + port->uart.custom_divisor = tmxbr * 8; + port->uart.uartclk = ioclk / 8; + goto timer_okay; + } + if (port->uart.custom_divisor / 32 <= 65535) { + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = port->uart.custom_divisor / 32; + port->uart.custom_divisor = tmxbr * 32; + port->uart.uartclk = ioclk / 32; + goto timer_okay; + } + + } else if (div_timer == MNSCx_DIV_TIMER_8BIT) { + if (port->uart.custom_divisor <= 255) { + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = port->uart.custom_divisor; + port->uart.uartclk = ioclk; + goto timer_okay; + } + if (port->uart.custom_divisor / 8 <= 255) { + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = port->uart.custom_divisor / 8; + port->uart.custom_divisor = tmxbr * 8; + port->uart.uartclk = ioclk / 8; + goto timer_okay; + } + if (port->uart.custom_divisor / 32 <= 255) { + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = port->uart.custom_divisor / 32; + port->uart.custom_divisor = tmxbr * 32; + port->uart.uartclk = ioclk / 32; + goto timer_okay; + } + } + } + + switch (div_timer) { + case MNSCx_DIV_TIMER_16BIT: + port->uart.uartclk = ioclk; + tmxmd = TM8MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + port->uart.uartclk = ioclk / 8; + tmxmd = TM8MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + + port->uart.uartclk = ioclk / 32; + tmxmd = TM8MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 65535) + goto timer_okay; + break; + + case MNSCx_DIV_TIMER_8BIT: + port->uart.uartclk = ioclk; + tmxmd = TM2MD_SRC_IOCLK; + tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + port->uart.uartclk = ioclk / 8; + tmxmd = TM2MD_SRC_IOCLK_8; + tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + + port->uart.uartclk = ioclk / 32; + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; + if (tmp > 0 && tmp <= 255) + goto timer_okay; + break; + + default: + BUG(); + return; + } + + /* refuse to change to a baud rate we can't support */ + _debug("CAN'T SUPPORT"); + + switch (battempt) { + case 0: + if (old) { + new->c_cflag &= ~CBAUD; + new->c_cflag |= (old->c_cflag & CBAUD); + battempt = 1; + goto try_alternative; + } + + case 1: + /* as a last resort, if the quotient is zero, default to 9600 + * bps */ + new->c_cflag &= ~CBAUD; + new->c_cflag |= B9600; + battempt = 2; + goto try_alternative; + + default: + /* hmmm... can't seem to support 9600 either + * - we could try iterating through the speeds we know about to + * find the lowest + */ + new->c_cflag &= ~CBAUD; + new->c_cflag |= B0; + + if (div_timer == MNSCx_DIV_TIMER_16BIT) + tmxmd = TM8MD_SRC_IOCLK_32; + else if (div_timer == MNSCx_DIV_TIMER_8BIT) + tmxmd = TM2MD_SRC_IOCLK_32; + tmxbr = 1; + + port->uart.uartclk = ioclk / 32; + break; + } +timer_okay: + + _debug("UARTCLK: %u / %hu", port->uart.uartclk, tmxbr); + + /* make the changes */ + spin_lock_irqsave(&port->uart.lock, flags); + + uart_update_timeout(&port->uart, new->c_cflag, baud); + + /* set the timer to produce the required baud rate */ + switch (div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *port->_tmxmd = 0; + *port->_tmxbr = tmxbr; + *port->_tmxmd = TM8MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *port->_tmxmd = 0; + *(volatile u8 *) port->_tmxbr = (u8) tmxbr; + *port->_tmxmd = TM2MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } + + /* CTS flow control flag and modem status interrupts */ + scxctr &= ~(SC2CTR_TWE | SC2CTR_TWS); + + if (port->type == PORT_MN10300_CTS && cflag & CRTSCTS) { + /* want to interrupt when CTS goes low if CTS is now + * high and vice versa + */ + port->tx_cts = *port->_status; + + if (port->tx_cts & SC2STR_CTS) + scxctr |= SC2CTR_TWE; + else + scxctr |= SC2CTR_TWE | SC2CTR_TWS; + } + + /* set up parity check flag */ + port->uart.read_status_mask = (1 << TTY_NORMAL) | (1 << TTY_OVERRUN); + if (new->c_iflag & INPCK) + port->uart.read_status_mask |= + (1 << TTY_PARITY) | (1 << TTY_FRAME); + if (new->c_iflag & (BRKINT | PARMRK)) + port->uart.read_status_mask |= (1 << TTY_BREAK); + + /* characters to ignore */ + port->uart.ignore_status_mask = 0; + if (new->c_iflag & IGNPAR) + port->uart.ignore_status_mask |= + (1 << TTY_PARITY) | (1 << TTY_FRAME); + if (new->c_iflag & IGNBRK) { + port->uart.ignore_status_mask |= (1 << TTY_BREAK); + /* + * If we're ignoring parity and break indicators, + * ignore overruns to (for real raw support). + */ + if (new->c_iflag & IGNPAR) + port->uart.ignore_status_mask |= (1 << TTY_OVERRUN); + } + + /* Ignore all characters if CREAD is not set */ + if ((new->c_cflag & CREAD) == 0) + port->uart.ignore_status_mask |= (1 << TTY_NORMAL); + + scxctr |= *port->_control & (SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); + *port->_control = scxctr; + + spin_unlock_irqrestore(&port->uart.lock, flags); +} + +/* + * set the terminal I/O parameters + */ +static void mn10300_serial_set_termios(struct uart_port *_port, + struct ktermios *new, + struct ktermios *old) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s,%p,%p", port->name, new, old); + + mn10300_serial_change_speed(port, new, old); + + /* handle turning off CRTSCTS */ + if (!(new->c_cflag & CRTSCTS)) { + u16 ctr = *port->_control; + ctr &= ~SC2CTR_TWE; + *port->_control = ctr; + } +} + +/* + * return description of port type + */ +static const char *mn10300_serial_type(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + if (port->uart.type == PORT_MN10300_CTS) + return "MN10300 SIF_CTS"; + + return "MN10300 SIF"; +} + +/* + * release I/O and memory regions in use by port + */ +static void mn10300_serial_release_port(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + release_mem_region((unsigned long) port->_iobase, 16); +} + +/* + * request I/O and memory regions for port + */ +static int mn10300_serial_request_port(struct uart_port *_port) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + request_mem_region((unsigned long) port->_iobase, 16, port->name); + return 0; +} + +/* + * configure the type and reserve the ports + */ +static void mn10300_serial_config_port(struct uart_port *_port, int type) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + + _enter("%s", port->name); + + port->uart.type = PORT_MN10300; + + if (port->options & MNSCx_OPT_CTS) + port->uart.type = PORT_MN10300_CTS; + + mn10300_serial_request_port(_port); +} + +/* + * verify serial parameters are suitable for this port type + */ +static int mn10300_serial_verify_port(struct uart_port *_port, + struct serial_struct *ss) +{ + struct mn10300_serial_port *port = + container_of(_port, struct mn10300_serial_port, uart); + void *mapbase = (void *) (unsigned long) port->uart.mapbase; + + _enter("%s", port->name); + + /* these things may not be changed */ + if (ss->irq != port->uart.irq || + ss->port != port->uart.iobase || + ss->io_type != port->uart.iotype || + ss->iomem_base != mapbase || + ss->iomem_reg_shift != port->uart.regshift || + ss->hub6 != port->uart.hub6 || + ss->xmit_fifo_size != port->uart.fifosize) + return -EINVAL; + + /* type may be changed on a port that supports CTS */ + if (ss->type != port->uart.type) { + if (!(port->options & MNSCx_OPT_CTS)) + return -EINVAL; + + if (ss->type != PORT_MN10300 && + ss->type != PORT_MN10300_CTS) + return -EINVAL; + } + + return 0; +} + +/* + * initialise the MN10300 on-chip UARTs + */ +static int __init mn10300_serial_init(void) +{ + struct mn10300_serial_port *port; + int ret, i; + + printk(KERN_INFO "%s version %s (%s)\n", + serial_name, serial_version, serial_revdate); + +#ifdef CONFIG_MN10300_TTYSM2 + SC2TIM = 8; /* make the baud base of timer 2 IOCLK/8 */ +#endif + + set_intr_stub(EXCEP_IRQ_LEVEL1, mn10300_serial_vdma_interrupt); + + ret = uart_register_driver(&mn10300_serial_driver); + if (!ret) { + for (i = 0 ; i < NR_PORTS ; i++) { + port = mn10300_serial_ports[i]; + if (!port || port->gdbstub) + continue; + + switch (port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + ret = uart_add_one_port(&mn10300_serial_driver, + &port->uart); + + if (ret < 0) { + _debug("ERROR %d", -ret); + break; + } + } + + if (ret) + uart_unregister_driver(&mn10300_serial_driver); + } + + return ret; +} + +__initcall(mn10300_serial_init); + + +#ifdef CONFIG_MN10300_TTYSM_CONSOLE + +/* + * print a string to the serial port without disturbing the real user of the + * port too much + * - the console must be locked by the caller + */ +static void mn10300_serial_console_write(struct console *co, + const char *s, unsigned count) +{ + struct mn10300_serial_port *port; + unsigned i; + u16 scxctr, txicr, tmp; + u8 tmxmd; + + port = mn10300_serial_ports[co->index]; + + /* firstly hijack the serial port from the "virtual DMA" controller */ + txicr = *port->tx_icr; + *port->tx_icr = GxICR_LEVEL_1; + tmp = *port->tx_icr; + + /* the transmitter may be disabled */ + scxctr = *port->_control; + if (!(scxctr & SC01CTR_TXE)) { + /* restart the UART clock */ + tmxmd = *port->_tmxmd; + + switch (port->div_timer) { + case MNSCx_DIV_TIMER_16BIT: + *port->_tmxmd = 0; + *port->_tmxmd = TM8MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; + break; + + case MNSCx_DIV_TIMER_8BIT: + *port->_tmxmd = 0; + *port->_tmxmd = TM2MD_INIT_COUNTER; + *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; + break; + } + + /* enable the transmitter */ + *port->_control = (scxctr & ~SC01CTR_BKE) | SC01CTR_TXE; + + } else if (scxctr & SC01CTR_BKE) { + /* stop transmitting BREAK */ + *port->_control = (scxctr & ~SC01CTR_BKE); + } + + /* send the chars into the serial port (with LF -> LFCR conversion) */ + for (i = 0; i < count; i++) { + char ch = *s++; + + while (*port->_status & SC01STR_TBF) + continue; + *(u8 *) port->_txb = ch; + + if (ch == 0x0a) { + while (*port->_status & SC01STR_TBF) + continue; + *(u8 *) port->_txb = 0xd; + } + } + + /* can't let the transmitter be turned off if it's actually + * transmitting */ + while (*port->_status & (SC01STR_TXF | SC01STR_TBF)) + continue; + + /* disable the transmitter if we re-enabled it */ + if (!(scxctr & SC01CTR_TXE)) + *port->_control = scxctr; + + *port->tx_icr = txicr; + tmp = *port->tx_icr; +} + +/* + * set up a serial port as a console + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * - return non-zero if we didn't find a serial port. + */ +static int __init mn10300_serial_console_setup(struct console *co, + char *options) +{ + struct mn10300_serial_port *port; + int i, parity = 'n', baud = 9600, bits = 8, flow = 0; + + for (i = 0 ; i < NR_PORTS ; i++) { + port = mn10300_serial_ports[i]; + if (port && !port->gdbstub && port->uart.line == co->index) + goto found_device; + } + + return -ENODEV; + +found_device: + switch (port->clock_src) { + case MNSCx_CLOCK_SRC_IOCLK: + port->ioclk = MN10300_IOCLK; + break; + +#ifdef MN10300_IOBCLK + case MNSCx_CLOCK_SRC_IOBCLK: + port->ioclk = MN10300_IOBCLK; + break; +#endif + default: + BUG(); + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&port->uart, co, baud, parity, bits, flow); +} + +/* + * register console + */ +static int __init mn10300_serial_console_init(void) +{ + register_console(&mn10300_serial_console); + return 0; +} + +console_initcall(mn10300_serial_console_init); +#endif diff --git a/arch/mn10300/kernel/mn10300-serial.h b/arch/mn10300/kernel/mn10300-serial.h new file mode 100644 index 00000000000..6796499bf78 --- /dev/null +++ b/arch/mn10300/kernel/mn10300-serial.h @@ -0,0 +1,126 @@ +/* MN10300 On-chip serial port driver definitions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _MN10300_SERIAL_H +#define _MN10300_SERIAL_H + +#ifndef __ASSEMBLY__ +#include <linux/serial_core.h> +#include <linux/termios.h> +#endif + +#include <asm/page.h> +#include <asm/serial-regs.h> + +#define NR_PORTS 3 /* should be set 3 or 9 or 16 */ + +#define MNSC_BUFFER_SIZE +(PAGE_SIZE / 2) + +/* intr_flags bits */ +#define MNSCx_RX_AVAIL 0x01 +#define MNSCx_RX_OVERF 0x02 +#define MNSCx_TX_SPACE 0x04 +#define MNSCx_TX_EMPTY 0x08 + +#ifndef __ASSEMBLY__ + +struct mn10300_serial_port { + char *rx_buffer; /* reception buffer base */ + unsigned rx_inp; /* pointer to rx input offset */ + unsigned rx_outp; /* pointer to rx output offset */ + u8 tx_xchar; /* high-priority XON/XOFF buffer */ + u8 tx_break; /* transmit break request */ + u8 intr_flags; /* interrupt flags */ + volatile u16 *rx_icr; /* Rx interrupt control register */ + volatile u16 *tx_icr; /* Tx interrupt control register */ + int rx_irq; /* reception IRQ */ + int tx_irq; /* transmission IRQ */ + int tm_irq; /* timer IRQ */ + + const char *name; /* name of serial port */ + const char *rx_name; /* Rx interrupt handler name of serial port */ + const char *tx_name; /* Tx interrupt handler name of serial port */ + const char *tm_name; /* Timer interrupt handler name */ + unsigned short type; /* type of serial port */ + unsigned char isconsole; /* T if it's a console */ + volatile void *_iobase; /* pointer to base of I/O control regs */ + volatile u16 *_control; /* control register pointer */ + volatile u8 *_status; /* status register pointer */ + volatile u8 *_intr; /* interrupt register pointer */ + volatile void *_rxb; /* receive buffer register pointer */ + volatile void *_txb; /* transmit buffer register pointer */ + volatile u16 *_tmicr; /* timer interrupt control register */ + volatile u8 *_tmxmd; /* baud rate timer mode register */ + volatile u16 *_tmxbr; /* baud rate timer base register */ + + /* this must come down here so that assembly can use BSET to access the + * above fields */ + struct uart_port uart; + + unsigned short rx_brk; /* current break reception status */ + u16 tx_cts; /* current CTS status */ + int gdbstub; /* preemptively stolen by GDB stub */ + + u8 clock_src; /* clock source */ +#define MNSCx_CLOCK_SRC_IOCLK 0 +#define MNSCx_CLOCK_SRC_IOBCLK 1 + + u8 div_timer; /* timer used as divisor */ +#define MNSCx_DIV_TIMER_16BIT 0 +#define MNSCx_DIV_TIMER_8BIT 1 + + u16 options; /* options */ +#define MNSCx_OPT_CTS 0x0001 + + unsigned long ioclk; /* base clock rate */ +}; + +#ifdef CONFIG_MN10300_TTYSM0 +extern struct mn10300_serial_port mn10300_serial_port_sif0; +#endif + +#ifdef CONFIG_MN10300_TTYSM1 +extern struct mn10300_serial_port mn10300_serial_port_sif1; +#endif + +#ifdef CONFIG_MN10300_TTYSM2 +extern struct mn10300_serial_port mn10300_serial_port_sif2; +#endif + +extern struct mn10300_serial_port *mn10300_serial_ports[]; + +struct mn10300_serial_int { + struct mn10300_serial_port *port; + asmlinkage void (*vdma)(void); +}; + +extern struct mn10300_serial_int mn10300_serial_int_tbl[]; + +extern asmlinkage void mn10300_serial_vdma_interrupt(void); +extern asmlinkage void mn10300_serial_vdma_rx_handler(void); +extern asmlinkage void mn10300_serial_vdma_tx_handler(void); + +#endif /* __ASSEMBLY__ */ + +#if defined(CONFIG_GDBSTUB_ON_TTYSM0) +#define SCgSTR SC0STR +#define SCgRXB SC0RXB +#define SCgRXIRQ SC0RXIRQ +#elif defined(CONFIG_GDBSTUB_ON_TTYSM1) +#define SCgSTR SC1STR +#define SCgRXB SC1RXB +#define SCgRXIRQ SC1RXIRQ +#elif defined(CONFIG_GDBSTUB_ON_TTYSM2) +#define SCgSTR SC2STR +#define SCgRXB SC2RXB +#define SCgRXIRQ SC2RXIRQ +#endif + +#endif /* _MN10300_SERIAL_H */ diff --git a/arch/mn10300/kernel/mn10300-watchdog-low.S b/arch/mn10300/kernel/mn10300-watchdog-low.S new file mode 100644 index 00000000000..996244745cc --- /dev/null +++ b/arch/mn10300/kernel/mn10300-watchdog-low.S @@ -0,0 +1,59 @@ +############################################################################### +# +# MN10300 Watchdog interrupt handler +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/intctl-regs.h> +#include <asm/timer-regs.h> +#include <asm/frame.inc> + + .text + +############################################################################### +# +# Watchdog handler entry point +# - special non-maskable interrupt +# +############################################################################### + .globl watchdog_handler + .type watchdog_handler,@function +watchdog_handler: + add -4,sp + SAVE_ALL + + mov 0xffffffff,d0 + mov d0,(REG_ORIG_D0,fp) + + mov fp,d0 + lsr 2,d1 + call watchdog_interrupt[],0 # watchdog_interrupt(regs,irq) + + jmp ret_from_intr + + .size watchdog_handler,.-watchdog_handler + +############################################################################### +# +# Watchdog touch entry point +# - kept to absolute minimum (unfortunately, it's prototyped in linux/nmi.h so +# we can't inline it) +# +############################################################################### + .globl touch_nmi_watchdog + .type touch_nmi_watchdog,@function +touch_nmi_watchdog: + clr d0 + mov d0,(watchdog_alert_counter) + ret [],0 + + .size touch_nmi_watchdog,.-touch_nmi_watchdog diff --git a/arch/mn10300/kernel/mn10300-watchdog.c b/arch/mn10300/kernel/mn10300-watchdog.c new file mode 100644 index 00000000000..10811e981d2 --- /dev/null +++ b/arch/mn10300/kernel/mn10300-watchdog.c @@ -0,0 +1,183 @@ +/* MN10300 Watchdog timer + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/kernel/nmi.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/nmi.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/atomic.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <asm/div64.h> +#include <asm/smp.h> +#include <asm/gdb-stub.h> +#include <asm/proc/clock.h> + +static DEFINE_SPINLOCK(watchdog_print_lock); +static unsigned int watchdog; +static unsigned int watchdog_hz = 1; +unsigned int watchdog_alert_counter; + +EXPORT_SYMBOL(touch_nmi_watchdog); + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check its timer makes IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are generated on every CPU, we only + * have to check the current processor. + * + * since NMIs dont listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ +static unsigned int last_irq_sums[NR_CPUS]; + +int __init check_watchdog(void) +{ + irq_cpustat_t tmp[1]; + + printk(KERN_INFO "Testing Watchdog... "); + + memcpy(tmp, irq_stat, sizeof(tmp)); + local_irq_enable(); + mdelay((10 * 1000) / watchdog_hz); /* wait 10 ticks */ + local_irq_disable(); + + if (nmi_count(0) - tmp[0].__nmi_count <= 5) { + printk(KERN_WARNING "CPU#%d: Watchdog appears to be stuck!\n", + 0); + return -1; + } + + printk(KERN_INFO "OK.\n"); + + /* now that we know it works we can reduce NMI frequency to + * something more reasonable; makes a difference in some configs + */ + watchdog_hz = 1; + + return 0; +} + +static int __init setup_watchdog(char *str) +{ + unsigned tmp; + int opt; + u8 ctr; + + get_option(&str, &opt); + if (opt != 1) + return 0; + + watchdog = opt; + if (watchdog) { + set_intr_stub(EXCEP_WDT, watchdog_handler); + ctr = WDCTR_WDCK_65536th; + WDCTR = WDCTR_WDRST | ctr; + WDCTR = ctr; + tmp = WDCTR; + + tmp = __muldiv64u(1 << (16 + ctr * 2), 1000000, MN10300_WDCLK); + tmp = 1000000000 / tmp; + watchdog_hz = (tmp + 500) / 1000; + } + + return 1; +} + +__setup("watchdog=", setup_watchdog); + +void __init watchdog_go(void) +{ + u8 wdt; + + if (watchdog) { + printk(KERN_INFO "Watchdog: running at %uHz\n", watchdog_hz); + wdt = WDCTR & ~WDCTR_WDCNE; + WDCTR = wdt | WDCTR_WDRST; + wdt = WDCTR; + WDCTR = wdt | WDCTR_WDCNE; + wdt = WDCTR; + + check_watchdog(); + } +} + +asmlinkage +void watchdog_interrupt(struct pt_regs *regs, enum exception_code excep) +{ + + /* + * Since current-> is always on the stack, and we always switch + * the stack NMI-atomically, it's safe to use smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + u8 wdt, tmp; + + wdt = WDCTR & ~WDCTR_WDCNE; + WDCTR = wdt; + tmp = WDCTR; + NMICR = NMICR_WDIF; + + nmi_count(cpu)++; + kstat_this_cpu.irqs[NMIIRQ]++; + sum = irq_stat[cpu].__irq_count; + + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + watchdog_alert_counter++; + if (watchdog_alert_counter == 5 * watchdog_hz) { + spin_lock(&watchdog_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk(KERN_ERR + "NMI Watchdog detected LOCKUP on CPU%d," + " pc %08lx, registers:\n", + cpu, regs->pc); + show_registers(regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&watchdog_print_lock); + bust_spinlocks(0); +#ifdef CONFIG_GDBSTUB + if (gdbstub_busy) + gdbstub_exception(regs, excep); + else + gdbstub_intercept(regs, excep); +#endif + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + watchdog_alert_counter = 0; + } + + WDCTR = wdt | WDCTR_WDRST; + tmp = WDCTR; + WDCTR = wdt | WDCTR_WDCNE; + tmp = WDCTR; +} diff --git a/arch/mn10300/kernel/mn10300_ksyms.c b/arch/mn10300/kernel/mn10300_ksyms.c new file mode 100644 index 00000000000..6d19628634e --- /dev/null +++ b/arch/mn10300/kernel/mn10300_ksyms.c @@ -0,0 +1,37 @@ +/* MN10300 Miscellaneous and library kernel exports + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <asm/uaccess.h> + + +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_change_bit); + +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memset); + +EXPORT_SYMBOL(strncpy_from_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(clear_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__generic_copy_from_user); +EXPORT_SYMBOL(__generic_copy_to_user); +EXPORT_SYMBOL(strnlen_user); + +extern u64 __ashrdi3(u64, unsigned); +extern u64 __ashldi3(u64, unsigned); +extern u64 __lshrdi3(u64, unsigned); +extern s64 __negdi2(s64); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__negdi2); diff --git a/arch/mn10300/kernel/module.c b/arch/mn10300/kernel/module.c new file mode 100644 index 00000000000..0e4d2f6fa6e --- /dev/null +++ b/arch/mn10300/kernel/module.c @@ -0,0 +1,206 @@ +/* MN10300 Kernel module helper routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by Mark Salter (msalter@redhat.com) + * - Derived from arch/i386/kernel/module.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public Licence as published by + * the Free Software Foundation; either version 2 of the Licence, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public Licence for more details. + * + * You should have received a copy of the GNU General Public Licence + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt, ...) +#endif + +/* + * allocate storage for a module + */ +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc_exec(size); +} + +/* + * free memory returned from module_alloc() + */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + * table entries. */ +} + +/* + * allow the arch to fix up the section table + * - we don't need anything special + */ +int module_frob_arch_sections(Elf_Ehdr *hdr, + Elf_Shdr *sechdrs, + char *secstrings, + struct module *mod) +{ + return 0; +} + +static uint32_t reloc_get16(uint8_t *p) +{ + return p[0] | (p[1] << 8); +} + +static uint32_t reloc_get24(uint8_t *p) +{ + return reloc_get16(p) | (p[2] << 16); +} + +static uint32_t reloc_get32(uint8_t *p) +{ + return reloc_get16(p) | (reloc_get16(p+2) << 16); +} + +static void reloc_put16(uint8_t *p, uint32_t val) +{ + p[0] = val & 0xff; + p[1] = (val >> 8) & 0xff; +} + +static void reloc_put24(uint8_t *p, uint32_t val) +{ + reloc_put16(p, val); + p[2] = (val >> 16) & 0xff; +} + +static void reloc_put32(uint8_t *p, uint32_t val) +{ + reloc_put16(p, val); + reloc_put16(p+2, val >> 16); +} + +/* + * apply a REL relocation + */ +int apply_relocate(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + printk(KERN_ERR "module %s: RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +/* + * apply a RELA relocation + */ +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + Elf32_Addr relocation; + uint8_t *location; + uint32_t value; + + DEBUGP("Applying relocate section %u to %u\n", + relsec, sechdrs[relsec].sh_info); + + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* this is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + + /* this is the symbol the relocation is referring to (note that + * all undefined symbols have been resolved by the caller) */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + + /* this is the adjustment to be made */ + relocation = sym->st_value + rel[i].r_addend; + + switch (ELF32_R_TYPE(rel[i].r_info)) { + /* for the first four relocation types, we add the + * adjustment into the value at the location given */ + case R_MN10300_32: + value = reloc_get32(location); + value += relocation; + reloc_put32(location, value); + break; + case R_MN10300_24: + value = reloc_get24(location); + value += relocation; + reloc_put24(location, value); + break; + case R_MN10300_16: + value = reloc_get16(location); + value += relocation; + reloc_put16(location, value); + break; + case R_MN10300_8: + *location += relocation; + break; + + /* for the next three relocation types, we write the + * adjustment with the address subtracted over the + * value at the location given */ + case R_MN10300_PCREL32: + value = relocation - (uint32_t) location; + reloc_put32(location, value); + break; + case R_MN10300_PCREL16: + value = relocation - (uint32_t) location; + reloc_put16(location, value); + break; + case R_MN10300_PCREL8: + *location = relocation - (uint32_t) location; + break; + + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +/* + * finish loading the module + */ +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +/* + * finish clearing the module + */ +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c new file mode 100644 index 00000000000..3b0d579fc15 --- /dev/null +++ b/arch/mn10300/kernel/process.c @@ -0,0 +1,297 @@ +/* MN10300 Process handling code + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/percpu.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/mmu_context.h> +#include <asm/fpu.h> +#include <asm/reset-regs.h> +#include <asm/gdb-stub.h> +#include "internal.h" + +/* + * power management idle function, if any.. + */ +void (*pm_idle)(void); +EXPORT_SYMBOL(pm_idle); + +/* + * return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + return ((unsigned long *) tsk->thread.sp)[3]; +} + +/* + * power off function, if any + */ +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + +/* + * we use this if we don't have any better idle routine + */ +static void default_idle(void) +{ + local_irq_disable(); + if (!need_resched()) + safe_halt(); + else + local_irq_enable(); +} + +/* + * the idle thread + * - there's no useful work to be done, so just try to conserve power and have + * a low exit latency (ie sit in a loop waiting for somebody to say that + * they'd like to reschedule) + */ +void cpu_idle(void) +{ + int cpu = smp_processor_id(); + + /* endless idle loop with no priority at all */ + for (;;) { + while (!need_resched()) { + void (*idle)(void); + + smp_rmb(); + idle = pm_idle; + if (!idle) + idle = default_idle; + + irq_stat[cpu].idle_timestamp = jiffies; + idle(); + } + + preempt_enable_no_resched(); + schedule(); + preempt_disable(); + } +} + +void release_segments(struct mm_struct *mm) +{ +} + +void machine_restart(char *cmd) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif + +#ifdef mn10300_unit_hard_reset + mn10300_unit_hard_reset(); +#else + mn10300_proc_hard_reset(); +#endif +} + +void machine_halt(void) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif +} + +void machine_power_off(void) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif +} + +void show_regs(struct pt_regs *regs) +{ +} + +/* + * create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + + regs.a2 = (unsigned long) fn; + regs.d2 = (unsigned long) arg; + regs.pc = (unsigned long) kernel_thread_helper; + local_save_flags(regs.epsw); + regs.epsw |= EPSW_IE | EPSW_IM_7; + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, + NULL, NULL); +} + +/* + * free current thread data structures etc.. + */ +void exit_thread(void) +{ + exit_fpu(); +} + +void flush_thread(void) +{ + flush_fpu(); +} + +void release_thread(struct task_struct *dead_task) +{ +} + +/* + * we do not have to muck with descriptors here, that is + * done in switch_mm() as needed. + */ +void copy_segments(struct task_struct *p, struct mm_struct *new_mm) +{ +} + +/* + * this gets called before we allocate a new thread and copy the current task + * into it so that we can store lazy state into memory + */ +void prepare_to_copy(struct task_struct *tsk) +{ + unlazy_fpu(tsk); +} + +/* + * set up the kernel stack for a new thread and copy arch-specific thread + * control information + */ +int copy_thread(int nr, unsigned long clone_flags, + unsigned long c_usp, unsigned long ustk_size, + struct task_struct *p, struct pt_regs *kregs) +{ + struct pt_regs *c_uregs, *c_kregs, *uregs; + unsigned long c_ksp; + + uregs = current->thread.uregs; + + c_ksp = (unsigned long) task_stack_page(p) + THREAD_SIZE; + + /* allocate the userspace exception frame and set it up */ + c_ksp -= sizeof(struct pt_regs); + c_uregs = (struct pt_regs *) c_ksp; + + p->thread.uregs = c_uregs; + *c_uregs = *uregs; + c_uregs->sp = c_usp; + c_uregs->epsw &= ~EPSW_FE; /* my FPU */ + + c_ksp -= 12; /* allocate function call ABI slack */ + + /* the new TLS pointer is passed in as arg #5 to sys_clone() */ + if (clone_flags & CLONE_SETTLS) + c_uregs->e2 = __frame->d3; + + /* set up the return kernel frame if called from kernel_thread() */ + c_kregs = c_uregs; + if (kregs != uregs) { + c_ksp -= sizeof(struct pt_regs); + c_kregs = (struct pt_regs *) c_ksp; + *c_kregs = *kregs; + c_kregs->sp = c_usp; + c_kregs->next = c_uregs; +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + c_kregs->e2 = (unsigned long) p; /* current */ +#endif + + c_ksp -= 12; /* allocate function call ABI slack */ + } + + /* set up things up so the scheduler can start the new task */ + p->thread.__frame = c_kregs; + p->thread.a3 = (unsigned long) c_kregs; + p->thread.sp = c_ksp; + p->thread.pc = (unsigned long) ret_from_fork; + p->thread.wchan = (unsigned long) ret_from_fork; + p->thread.usp = c_usp; + + return 0; +} + +/* + * clone a process + * - tlsptr is retrieved by copy_thread() from __frame->d3 + */ +asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp, + int __user *parent_tidptr, int __user *child_tidptr, + int __user *tlsptr) +{ + return do_fork(clone_flags, newsp ?: __frame->sp, __frame, 0, + parent_tidptr, child_tidptr); +} + +asmlinkage long sys_fork(void) +{ + return do_fork(SIGCHLD, __frame->sp, __frame, 0, NULL, NULL); +} + +asmlinkage long sys_vfork(void) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, __frame->sp, __frame, + 0, NULL, NULL); +} + +asmlinkage long sys_execve(char __user *name, + char __user * __user *argv, + char __user * __user *envp) +{ + char *filename; + int error; + + lock_kernel(); + + filename = getname(name); + error = PTR_ERR(filename); + if (!IS_ERR(filename)) { + error = do_execve(filename, argv, envp, __frame); + if (error == 0) + current->ptrace &= ~PT_DTRACE; + + putname(filename); + } + + unlock_kernel(); + return error; +} + +unsigned long get_wchan(struct task_struct *p) +{ + return p->thread.wchan; +} diff --git a/arch/mn10300/kernel/profile-low.S b/arch/mn10300/kernel/profile-low.S new file mode 100644 index 00000000000..94ffac12d02 --- /dev/null +++ b/arch/mn10300/kernel/profile-low.S @@ -0,0 +1,72 @@ +############################################################################### +# +# Fast profiling interrupt handler +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/segment.h> +#include <asm/smp.h> +#include <asm/intctl-regs.h> +#include <asm/timer-regs.h> + +#define pi break + + .balign 4 +counter: + .long -1 + +############################################################################### +# +# Profiling interrupt entry point +# - intended to run at interrupt priority 1 +# +############################################################################### +ENTRY(profile_handler) + movm [d2,d3,a2],(sp) + + # ignore userspace + mov (12,sp),d2 + and EPSW_nSL,d2 + bne out + + # do nothing if there's no buffer + mov (prof_buffer),a2 + and a2,a2 + beq out + or 0x20000000,a2 + + # calculate relative position in text segment + mov (16,sp),d2 + sub _stext,d2 + mov (prof_shift),d3 + lsr d3,d2 + mov (prof_len),d3 + cmp d3,d2 + bcc outside_text + + # increment the appropriate profile bucket +do_inc: + asl2 d2 + mov (a2,d2),d3 + inc d3 + mov d3,(a2,d2) +out: + mov GxICR_DETECT,d2 + movbu d2,(TM11ICR) # ACK the interrupt + movbu (TM11ICR),d2 + movm (sp),[d2,d3,a2] + rti + +outside_text: + sub 1,d3 + mov d3,d2 + bra do_inc diff --git a/arch/mn10300/kernel/profile.c b/arch/mn10300/kernel/profile.c new file mode 100644 index 00000000000..20d7d0306b1 --- /dev/null +++ b/arch/mn10300/kernel/profile.c @@ -0,0 +1,51 @@ +/* MN10300 Profiling setup + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * initialise the profiling if enabled + * - using with gdbstub will give anomalous results + * - can't be used with gdbstub if running at IRQ priority 0 + */ +static __init int profile_init(void) +{ + u16 tmp; + + if (!prof_buffer) + return 0; + + /* use timer 11 to drive the profiling interrupts */ + set_intr_stub(EXCEP_IRQ_LEVEL0, profile_handler); + + /* set IRQ priority at which to run */ + set_intr_level(TM11IRQ, GxICR_LEVEL_0); + + /* set up timer 11 + * - source: (IOCLK 33MHz)*2 = 66MHz + * - frequency: (33330000*2) / 8 / 20625 = 202Hz + */ + TM11BR = 20625 - 1; + TM11MD = TM8MD_SRC_IOCLK_8; + TM11MD |= TM8MD_INIT_COUNTER; + TM11MD &= ~TM8MD_INIT_COUNTER; + TM11MD |= TM8MD_COUNT_ENABLE; + + TM11ICR |= GxICR_ENABLE; + tmp = TM11ICR; + + printk(KERN_INFO "Profiling initiated on timer 11, priority 0, %uHz\n", + mn10300_ioclk / 8 / (TM11BR + 1)); + printk(KERN_INFO "Profile histogram stored %p-%p\n", + prof_buffer, (u8 *)(prof_buffer + prof_len) - 1); + + return 0; +} + +__initcall(profile_init); diff --git a/arch/mn10300/kernel/ptrace.c b/arch/mn10300/kernel/ptrace.c new file mode 100644 index 00000000000..d6d6cdc75c5 --- /dev/null +++ b/arch/mn10300/kernel/ptrace.c @@ -0,0 +1,379 @@ +/* MN10300 Process tracing + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/fpu.h> +#include <asm/asm-offsets.h> + +/* + * translate ptrace register IDs into struct pt_regs offsets + */ +static const u8 ptrace_regid_to_frame[] = { + [PT_A3 << 2] = REG_A3, + [PT_A2 << 2] = REG_A2, + [PT_D3 << 2] = REG_D3, + [PT_D2 << 2] = REG_D2, + [PT_MCVF << 2] = REG_MCVF, + [PT_MCRL << 2] = REG_MCRL, + [PT_MCRH << 2] = REG_MCRH, + [PT_MDRQ << 2] = REG_MDRQ, + [PT_E1 << 2] = REG_E1, + [PT_E0 << 2] = REG_E0, + [PT_E7 << 2] = REG_E7, + [PT_E6 << 2] = REG_E6, + [PT_E5 << 2] = REG_E5, + [PT_E4 << 2] = REG_E4, + [PT_E3 << 2] = REG_E3, + [PT_E2 << 2] = REG_E2, + [PT_SP << 2] = REG_SP, + [PT_LAR << 2] = REG_LAR, + [PT_LIR << 2] = REG_LIR, + [PT_MDR << 2] = REG_MDR, + [PT_A1 << 2] = REG_A1, + [PT_A0 << 2] = REG_A0, + [PT_D1 << 2] = REG_D1, + [PT_D0 << 2] = REG_D0, + [PT_ORIG_D0 << 2] = REG_ORIG_D0, + [PT_EPSW << 2] = REG_EPSW, + [PT_PC << 2] = REG_PC, +}; + +static inline int get_stack_long(struct task_struct *task, int offset) +{ + return *(unsigned long *) + ((unsigned long) task->thread.uregs + offset); +} + +/* + * this routine will put a word on the processes privileged stack. + * the offset is how far from the base addr as stored in the TSS. + * this routine assumes that all the privileged stacks are in our + * data space. + */ +static inline +int put_stack_long(struct task_struct *task, int offset, unsigned long data) +{ + unsigned long stack; + + stack = (unsigned long) task->thread.uregs + offset; + *(unsigned long *) stack = data; + return 0; +} + +static inline unsigned long get_fpregs(struct fpu_state_struct *buf, + struct task_struct *tsk) +{ + return __copy_to_user(buf, &tsk->thread.fpu_state, + sizeof(struct fpu_state_struct)); +} + +static inline unsigned long set_fpregs(struct task_struct *tsk, + struct fpu_state_struct *buf) +{ + return __copy_from_user(&tsk->thread.fpu_state, buf, + sizeof(struct fpu_state_struct)); +} + +static inline void fpsave_init(struct task_struct *task) +{ + memset(&task->thread.fpu_state, 0, sizeof(struct fpu_state_struct)); +} + +/* + * make sure the single step bit is not set + */ +void ptrace_disable(struct task_struct *child) +{ +#ifndef CONFIG_MN10300_USING_JTAG + struct user *dummy = NULL; + long tmp; + + tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw); + tmp &= ~EPSW_T; + put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp); +#endif +} + +/* + * set the single step bit + */ +void ptrace_enable(struct task_struct *child) +{ +#ifndef CONFIG_MN10300_USING_JTAG + struct user *dummy = NULL; + long tmp; + + tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw); + tmp |= EPSW_T; + put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp); +#endif +} + +/* + * handle the arch-specific side of process tracing + */ +long arch_ptrace(struct task_struct *child, long request, long addr, long data) +{ + struct fpu_state_struct fpu_state; + int i, ret; + + switch (request) { + /* read the word at location addr. */ + case PTRACE_PEEKTEXT: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp, (unsigned long *) data); + break; + } + + /* read the word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp, (unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + tmp = 0; /* Default return condition */ + if (addr < NR_PTREGS << 2) + tmp = get_stack_long(child, + ptrace_regid_to_frame[addr]); + ret = put_user(tmp, (unsigned long *) data); + break; + } + + /* write the word at location addr. */ + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + if (access_process_vm(child, addr, &data, sizeof(data), 1) == + sizeof(data)) + ret = 0; + else + ret = -EIO; + break; + + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + ret = 0; + if (addr < NR_PTREGS << 2) + ret = put_stack_long(child, ptrace_regid_to_frame[addr], + data); + break; + + /* continue and stop at next (return from) syscall */ + case PTRACE_SYSCALL: + /* restart after signal. */ + case PTRACE_CONT: + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + ptrace_disable(child); + wake_up_process(child); + ret = 0; + break; + + /* + * make the child exit + * - the best I can do is send it a sigkill + * - perhaps it should be put in the status that it wants to + * exit + */ + case PTRACE_KILL: + ret = 0; + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + clear_tsk_thread_flag(child, TIF_SINGLESTEP); + ptrace_disable(child); + wake_up_process(child); + break; + + case PTRACE_SINGLESTEP: /* set the trap flag. */ +#ifndef CONFIG_MN10300_USING_JTAG + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + ptrace_enable(child); + child->exit_code = data; + wake_up_process(child); + ret = 0; +#else + ret = -EINVAL; +#endif + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + + /* Get all gp regs from the child. */ + case PTRACE_GETREGS: { + unsigned long tmp; + + if (!access_ok(VERIFY_WRITE, (unsigned *) data, NR_PTREGS << 2)) { + ret = -EIO; + break; + } + + for (i = 0; i < NR_PTREGS << 2; i += 4) { + tmp = get_stack_long(child, ptrace_regid_to_frame[i]); + __put_user(tmp, (unsigned long *) data); + data += sizeof(tmp); + } + ret = 0; + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + unsigned long tmp; + + if (!access_ok(VERIFY_READ, (unsigned long *)data, + sizeof(struct pt_regs))) { + ret = -EIO; + break; + } + + for (i = 0; i < NR_PTREGS << 2; i += 4) { + __get_user(tmp, (unsigned long *) data); + put_stack_long(child, ptrace_regid_to_frame[i], tmp); + data += sizeof(tmp); + } + ret = 0; + break; + } + + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + if (is_using_fpu(child)) { + unlazy_fpu(child); + fpu_state = child->thread.fpu_state; + } else { + memset(&fpu_state, 0, sizeof(fpu_state)); + } + + ret = -EIO; + if (copy_to_user((void *) data, &fpu_state, + sizeof(fpu_state)) == 0) + ret = 0; + break; + } + + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + ret = -EFAULT; + if (copy_from_user(&fpu_state, (const void *) data, + sizeof(fpu_state)) == 0) { + fpu_kill_state(child); + child->thread.fpu_state = fpu_state; + set_using_fpu(child); + ret = 0; + } + break; + } + + case PTRACE_SETOPTIONS: { + if (data & PTRACE_O_TRACESYSGOOD) + child->ptrace |= PT_TRACESYSGOOD; + else + child->ptrace &= ~PT_TRACESYSGOOD; + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } + + return ret; +} + +/* + * notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) +{ +#if 0 + /* just in case... */ + printk(KERN_DEBUG "[%d] syscall_%lu(%lx,%lx,%lx,%lx) = %lx\n", + current->pid, + regs->orig_d0, + regs->a0, + regs->d1, + regs->a3, + regs->a2, + regs->d0); + return; +#endif + + if (!test_thread_flag(TIF_SYSCALL_TRACE) && + !test_thread_flag(TIF_SINGLESTEP)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + + /* the 0x80 provides a way for the tracing parent to distinguish + between a syscall stop and SIGTRAP delivery */ + ptrace_notify(SIGTRAP | + ((current->ptrace & PT_TRACESYSGOOD) && + !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/mn10300/kernel/rtc.c b/arch/mn10300/kernel/rtc.c new file mode 100644 index 00000000000..042f792d843 --- /dev/null +++ b/arch/mn10300/kernel/rtc.c @@ -0,0 +1,173 @@ +/* MN10300 RTC management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mc146818rtc.h> +#include <linux/bcd.h> +#include <linux/timex.h> +#include <asm/rtc-regs.h> +#include <asm/rtc.h> + +DEFINE_SPINLOCK(rtc_lock); +EXPORT_SYMBOL(rtc_lock); + +/* last time the RTC got updated */ +static long last_rtc_update; + +/* time for RTC to update itself in ioclks */ +static unsigned long mn10300_rtc_update_period; + +/* + * read the current RTC time + */ +unsigned long __init get_initial_rtc_time(void) +{ + struct rtc_time tm; + + get_rtc_time(&tm); + + return mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +/* + * In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500 + * ms after the second nowtime has started, because when nowtime is written + * into the registers of the CMOS clock, it will jump to the next second + * precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data + * sheet for details. + * + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you'll only notice that after reboot! + */ +static int set_rtc_mmss(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + + /* gets recalled with irq locally disabled */ + spin_lock(&rtc_lock); + save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being + * set */ + CMOS_WRITE(save_control | RTC_SET, RTC_CONTROL); + + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset + * prescaler */ + CMOS_WRITE(save_freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT); + + cmos_minutes = CMOS_READ(RTC_MINUTES); + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) + /* correct for half hour time zone */ + real_minutes += 30; + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + } + CMOS_WRITE(real_seconds, RTC_SECONDS); + CMOS_WRITE(real_minutes, RTC_MINUTES); + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock(&rtc_lock); + + return retval; +} + +void check_rtc_time(void) +{ + /* the RTC clock just finished ticking over again this second + * - if we have an externally synchronized Linux clock, then update + * RTC clock accordingly every ~11 minutes. set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_nsec / 1000 >= 500000 - ((unsigned) TICK_SIZE) / 2 && + xtime.tv_nsec / 1000 <= 500000 + ((unsigned) TICK_SIZE) / 2 + ) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + /* do it again in 60s */ + last_rtc_update = xtime.tv_sec - 600; + } +} + +/* + * calibrate the TSC clock against the RTC + */ +void __init calibrate_clock(void) +{ + unsigned long count0, counth, count1; + unsigned char status; + + /* make sure the RTC is running and is set to operate in 24hr mode */ + status = RTSRC; + RTCRB |= RTCRB_SET; + RTCRB |= RTCRB_TM_24HR; + RTCRA |= RTCRA_DVR; + RTCRA &= ~RTCRA_DVR; + RTCRB &= ~RTCRB_SET; + + /* work out the clock speed by counting clock cycles between ends of + * the RTC update cycle - track the RTC through one complete update + * cycle (1 second) + */ + startup_timestamp_counter(); + + while (!(RTCRA & RTCRA_UIP)) {} + while ((RTCRA & RTCRA_UIP)) {} + + count0 = TMTSCBC; + + while (!(RTCRA & RTCRA_UIP)) {} + + counth = TMTSCBC; + + while ((RTCRA & RTCRA_UIP)) {} + + count1 = TMTSCBC; + + shutdown_timestamp_counter(); + + MN10300_TSCCLK = count0 - count1; /* the timers count down */ + mn10300_rtc_update_period = counth - count1; + MN10300_TSC_PER_HZ = MN10300_TSCCLK / HZ; +} diff --git a/arch/mn10300/kernel/semaphore.c b/arch/mn10300/kernel/semaphore.c new file mode 100644 index 00000000000..9153c4039fd --- /dev/null +++ b/arch/mn10300/kernel/semaphore.c @@ -0,0 +1,149 @@ +/* MN10300 Semaphore implementation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/module.h> +#include <asm/semaphore.h> + +struct sem_waiter { + struct list_head list; + struct task_struct *task; +}; + +#if SEMAPHORE_DEBUG +void semtrace(struct semaphore *sem, const char *str) +{ + if (sem->debug) + printk(KERN_DEBUG "[%d] %s({%d,%d})\n", + current->pid, + str, + atomic_read(&sem->count), + list_empty(&sem->wait_list) ? 0 : 1); +} +#else +#define semtrace(SEM, STR) do { } while (0) +#endif + +/* + * wait for a token to be granted from a semaphore + * - entered with lock held and interrupts disabled + */ +void __down(struct semaphore *sem, unsigned long flags) +{ + struct task_struct *tsk = current; + struct sem_waiter waiter; + + semtrace(sem, "Entering __down"); + + /* set up my own style of waitqueue */ + waiter.task = tsk; + get_task_struct(tsk); + + list_add_tail(&waiter.list, &sem->wait_list); + + /* we don't need to touch the semaphore struct anymore */ + spin_unlock_irqrestore(&sem->wait_lock, flags); + + /* wait to be given the semaphore */ + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + + for (;;) { + if (!waiter.task) + break; + schedule(); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + } + + tsk->state = TASK_RUNNING; + semtrace(sem, "Leaving __down"); +} +EXPORT_SYMBOL(__down); + +/* + * interruptibly wait for a token to be granted from a semaphore + * - entered with lock held and interrupts disabled + */ +int __down_interruptible(struct semaphore *sem, unsigned long flags) +{ + struct task_struct *tsk = current; + struct sem_waiter waiter; + int ret; + + semtrace(sem, "Entering __down_interruptible"); + + /* set up my own style of waitqueue */ + waiter.task = tsk; + get_task_struct(tsk); + + list_add_tail(&waiter.list, &sem->wait_list); + + /* we don't need to touch the semaphore struct anymore */ + set_task_state(tsk, TASK_INTERRUPTIBLE); + + spin_unlock_irqrestore(&sem->wait_lock, flags); + + /* wait to be given the semaphore */ + ret = 0; + for (;;) { + if (!waiter.task) + break; + if (unlikely(signal_pending(current))) + goto interrupted; + schedule(); + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + + out: + tsk->state = TASK_RUNNING; + semtrace(sem, "Leaving __down_interruptible"); + return ret; + + interrupted: + spin_lock_irqsave(&sem->wait_lock, flags); + list_del(&waiter.list); + spin_unlock_irqrestore(&sem->wait_lock, flags); + + ret = 0; + if (!waiter.task) { + put_task_struct(current); + ret = -EINTR; + } + goto out; +} +EXPORT_SYMBOL(__down_interruptible); + +/* + * release a single token back to a semaphore + * - entered with lock held and interrupts disabled + */ +void __up(struct semaphore *sem) +{ + struct task_struct *tsk; + struct sem_waiter *waiter; + + semtrace(sem, "Entering __up"); + + /* grant the token to the process at the front of the queue */ + waiter = list_entry(sem->wait_list.next, struct sem_waiter, list); + + /* We must be careful not to touch 'waiter' after we set ->task = NULL. + * It is an allocated on the waiter's stack and may become invalid at + * any time after that point (due to a wakeup from another source). + */ + list_del_init(&waiter->list); + tsk = waiter->task; + smp_mb(); + waiter->task = NULL; + wake_up_process(tsk); + put_task_struct(tsk); + + semtrace(sem, "Leaving __up"); +} +EXPORT_SYMBOL(__up); diff --git a/arch/mn10300/kernel/setup.c b/arch/mn10300/kernel/setup.c new file mode 100644 index 00000000000..6b7ce263685 --- /dev/null +++ b/arch/mn10300/kernel/setup.c @@ -0,0 +1,298 @@ +/* MN10300 Arch-specific initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/seq_file.h> +#include <asm/processor.h> +#include <linux/console.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/setup.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/proc/proc.h> +#include <asm/busctl-regs.h> +#include <asm/fpu.h> +#include <asm/sections.h> + +struct mn10300_cpuinfo boot_cpu_data; + +/* For PCI or other memory-mapped resources */ +unsigned long pci_mem_start = 0x18000000; + +char redboot_command_line[COMMAND_LINE_SIZE] = + "console=ttyS0,115200 root=/dev/mtdblock3 rw"; + +char __initdata redboot_platform_name[COMMAND_LINE_SIZE]; + +static struct resource code_resource = { + .start = 0x100000, + .end = 0, + .name = "Kernel code", +}; + +static struct resource data_resource = { + .start = 0, + .end = 0, + .name = "Kernel data", +}; + +static unsigned long __initdata phys_memory_base; +static unsigned long __initdata phys_memory_end; +static unsigned long __initdata memory_end; +unsigned long memory_size; + +struct thread_info *__current_ti = &init_thread_union.thread_info; +struct task_struct *__current = &init_task; + +#define mn10300_known_cpus 3 +static const char *const mn10300_cputypes[] = { + "am33v1", + "am33v2", + "am34v1", + "unknown" +}; + +/* + * + */ +static void __init parse_mem_cmdline(char **cmdline_p) +{ + char *from, *to, c; + + /* save unparsed command line copy for /proc/cmdline */ + strcpy(boot_command_line, redboot_command_line); + + /* see if there's an explicit memory size option */ + from = redboot_command_line; + to = redboot_command_line; + c = ' '; + + for (;;) { + if (c == ' ' && !memcmp(from, "mem=", 4)) { + if (to != redboot_command_line) + to--; + memory_size = memparse(from + 4, &from); + } + + c = *(from++); + if (!c) + break; + + *(to++) = c; + } + + *to = '\0'; + *cmdline_p = redboot_command_line; + + if (memory_size == 0) + panic("Memory size not known\n"); + + memory_end = (unsigned long) CONFIG_KERNEL_RAM_BASE_ADDRESS + + memory_size; + if (memory_end > phys_memory_end) + memory_end = phys_memory_end; +} + +/* + * architecture specific setup + */ +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long kstart_pfn, start_pfn, free_pfn, end_pfn; + + cpu_init(); + unit_setup(); + parse_mem_cmdline(cmdline_p); + + init_mm.start_code = (unsigned long)&_text; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = virt_to_bus(&_text); + code_resource.end = virt_to_bus(&_etext)-1; + data_resource.start = virt_to_bus(&_etext); + data_resource.end = virt_to_bus(&_edata)-1; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + + start_pfn = (CONFIG_KERNEL_RAM_BASE_ADDRESS >> PAGE_SHIFT); + kstart_pfn = PFN_UP(__pa(&_text)); + free_pfn = PFN_UP(__pa(&_end)); + end_pfn = PFN_DOWN(__pa(memory_end)); + + bootmap_size = init_bootmem_node(&contig_page_data, + free_pfn, + start_pfn, + end_pfn); + + if (kstart_pfn > start_pfn) + free_bootmem(PFN_PHYS(start_pfn), + PFN_PHYS(kstart_pfn - start_pfn)); + + free_bootmem(PFN_PHYS(free_pfn), + PFN_PHYS(end_pfn - free_pfn)); + + /* If interrupt vector table is in main ram, then we need to + reserve the page it is occupying. */ + if (CONFIG_INTERRUPT_VECTOR_BASE >= CONFIG_KERNEL_RAM_BASE_ADDRESS && + CONFIG_INTERRUPT_VECTOR_BASE < memory_end) + reserve_bootmem(CONFIG_INTERRUPT_VECTOR_BASE, 1, + BOOTMEM_DEFAULT); + + reserve_bootmem(PAGE_ALIGN(PFN_PHYS(free_pfn)), bootmap_size, + BOOTMEM_DEFAULT); + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + + paging_init(); +} + +/* + * perform CPU initialisation + */ +void __init cpu_init(void) +{ + unsigned long cpurev = CPUREV, type; + unsigned long base, size; + + type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S; + if (type > mn10300_known_cpus) + type = mn10300_known_cpus; + + printk(KERN_INFO "Matsushita %s, rev %ld\n", + mn10300_cputypes[type], + (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S); + + /* determine the memory size and base from the memory controller regs */ + memory_size = 0; + + base = SDBASE(0); + if (base & SDBASE_CE) { + size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT; + size = ~size + 1; + base &= SDBASE_CBA; + + printk(KERN_INFO "SDRAM[0]: %luMb @%08lx\n", size >> 20, base); + memory_size += size; + phys_memory_base = base; + } + + base = SDBASE(1); + if (base & SDBASE_CE) { + size = (base & SDBASE_CBAM) << SDBASE_CBAM_SHIFT; + size = ~size + 1; + base &= SDBASE_CBA; + + printk(KERN_INFO "SDRAM[1]: %luMb @%08lx\n", size >> 20, base); + memory_size += size; + if (phys_memory_base == 0) + phys_memory_base = base; + } + + phys_memory_end = phys_memory_base + memory_size; + +#ifdef CONFIG_FPU + fpu_init_state(); +#endif +} + +/* + * Get CPU information for use by the procfs. + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ + unsigned long cpurev = CPUREV, type, icachesz, dcachesz; + + type = (CPUREV & CPUREV_TYPE) >> CPUREV_TYPE_S; + if (type > mn10300_known_cpus) + type = mn10300_known_cpus; + + icachesz = + ((cpurev & CPUREV_ICWAY ) >> CPUREV_ICWAY_S) * + ((cpurev & CPUREV_ICSIZE) >> CPUREV_ICSIZE_S) * + 1024; + + dcachesz = + ((cpurev & CPUREV_DCWAY ) >> CPUREV_DCWAY_S) * + ((cpurev & CPUREV_DCSIZE) >> CPUREV_DCSIZE_S) * + 1024; + + seq_printf(m, + "processor : 0\n" + "vendor_id : Matsushita\n" + "cpu core : %s\n" + "cpu rev : %lu\n" + "model name : " PROCESSOR_MODEL_NAME "\n" + "icache size: %lu\n" + "dcache size: %lu\n", + mn10300_cputypes[type], + (cpurev & CPUREV_REVISION) >> CPUREV_REVISION_S, + icachesz, + dcachesz + ); + + seq_printf(m, + "ioclk speed: %lu.%02luMHz\n" + "bogomips : %lu.%02lu\n\n", + MN10300_IOCLK / 1000000, + (MN10300_IOCLK / 10000) % 100, + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100 + ); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? cpu_data + *pos : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; diff --git a/arch/mn10300/kernel/sigframe.h b/arch/mn10300/kernel/sigframe.h new file mode 100644 index 00000000000..0decba28ae8 --- /dev/null +++ b/arch/mn10300/kernel/sigframe.h @@ -0,0 +1,33 @@ +/* MN10300 Signal frame definitions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +struct sigframe +{ + void (*pretcode)(void); + int sig; + struct sigcontext *psc; + struct sigcontext sc; + struct fpucontext fpuctx; + unsigned long extramask[_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe +{ + void (*pretcode)(void); + int sig; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + struct fpucontext fpuctx; + char retcode[8]; +}; diff --git a/arch/mn10300/kernel/signal.c b/arch/mn10300/kernel/signal.c new file mode 100644 index 00000000000..841ca9955a1 --- /dev/null +++ b/arch/mn10300/kernel/signal.c @@ -0,0 +1,564 @@ +/* MN10300 Signal handling + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/tty.h> +#include <linux/personality.h> +#include <linux/suspend.h> +#include <asm/cacheflush.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> +#include <asm/fpu.h> +#include "sigframe.h" + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* + * atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage long sys_sigsuspend(int history0, int history1, old_sigset_t mask) +{ + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + current->saved_sigmask = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); + return -ERESTARTNOHAND; +} + +/* + * set signal action syscall + */ +asmlinkage long sys_sigaction(int sig, + const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +/* + * set alternate signal stack syscall + */ +asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t *uoss) +{ + return do_sigaltstack(uss, uoss, __frame->sp); +} + +/* + * do a signal return; undo the signal stack. + */ +static int restore_sigcontext(struct pt_regs *regs, + struct sigcontext __user *sc, long *_d0) +{ + unsigned int err = 0; + + if (is_using_fpu(current)) + fpu_kill_state(current); + +#define COPY(x) err |= __get_user(regs->x, &sc->x) + COPY(d1); COPY(d2); COPY(d3); + COPY(a0); COPY(a1); COPY(a2); COPY(a3); + COPY(e0); COPY(e1); COPY(e2); COPY(e3); + COPY(e4); COPY(e5); COPY(e6); COPY(e7); + COPY(lar); COPY(lir); + COPY(mdr); COPY(mdrq); + COPY(mcvf); COPY(mcrl); COPY(mcrh); + COPY(sp); COPY(pc); +#undef COPY + + { + unsigned int tmpflags; +#ifndef CONFIG_MN10300_USING_JTAG +#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \ + EPSW_T | EPSW_nAR) +#else +#define USER_EPSW (EPSW_FLAG_Z | EPSW_FLAG_N | EPSW_FLAG_C | EPSW_FLAG_V | \ + EPSW_nAR) +#endif + err |= __get_user(tmpflags, &sc->epsw); + regs->epsw = (regs->epsw & ~USER_EPSW) | + (tmpflags & USER_EPSW); + regs->orig_d0 = -1; /* disable syscall checks */ + } + + { + struct fpucontext *buf; + err |= __get_user(buf, &sc->fpucontext); + if (buf) { + if (verify_area(VERIFY_READ, buf, sizeof(*buf))) + goto badframe; + err |= fpu_restore_sigcontext(buf); + } + } + + err |= __get_user(*_d0, &sc->d0); + return err; + +badframe: + return 1; +} + +/* + * standard signal return syscall + */ +asmlinkage long sys_sigreturn(void) +{ + struct sigframe __user *frame = (struct sigframe __user *) __frame->sp; + sigset_t set; + long d0; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask)) + goto badframe; + + if (_NSIG_WORDS > 1 && + __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(__frame, &frame->sc, &d0)) + goto badframe; + + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * realtime signal return syscall + */ +asmlinkage long sys_rt_sigreturn(void) +{ + struct rt_sigframe __user *frame = + (struct rt_sigframe __user *) __frame->sp; + sigset_t set; + unsigned long d0; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(__frame, &frame->uc.uc_mcontext, &d0)) + goto badframe; + + if (do_sigaltstack(&frame->uc.uc_stack, NULL, __frame->sp) == -EFAULT) + goto badframe; + + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * store the userspace context into a signal frame + */ +static int setup_sigcontext(struct sigcontext __user *sc, + struct fpucontext *fpuctx, + struct pt_regs *regs, + unsigned long mask) +{ + int tmp, err = 0; + +#define COPY(x) err |= __put_user(regs->x, &sc->x) + COPY(d0); COPY(d1); COPY(d2); COPY(d3); + COPY(a0); COPY(a1); COPY(a2); COPY(a3); + COPY(e0); COPY(e1); COPY(e2); COPY(e3); + COPY(e4); COPY(e5); COPY(e6); COPY(e7); + COPY(lar); COPY(lir); + COPY(mdr); COPY(mdrq); + COPY(mcvf); COPY(mcrl); COPY(mcrh); + COPY(sp); COPY(epsw); COPY(pc); +#undef COPY + + tmp = fpu_setup_sigcontext(fpuctx); + if (tmp < 0) + err = 1; + else + err |= __put_user(tmp ? fpuctx : NULL, &sc->fpucontext); + + /* non-iBCS2 extensions.. */ + err |= __put_user(mask, &sc->oldmask); + + return err; +} + +/* + * determine which stack to use.. + */ +static inline void __user *get_sigframe(struct k_sigaction *ka, + struct pt_regs *regs, + size_t frame_size) +{ + unsigned long sp; + + /* default to using normal stack */ + sp = regs->sp; + + /* this is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (!on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + return (void __user *) ((sp - frame_size) & ~7UL); +} + +/* + * set up a normal signal frame + */ +static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs *regs) +{ + struct sigframe __user *frame; + int rsig; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + rsig = sig; + if (sig < 32 && + current_thread_info()->exec_domain && + current_thread_info()->exec_domain->signal_invmap) + rsig = current_thread_info()->exec_domain->signal_invmap[sig]; + + if (__put_user(rsig, &frame->sig) < 0 || + __put_user(&frame->sc, &frame->psc) < 0) + goto give_sigsegv; + + if (setup_sigcontext(&frame->sc, &frame->fpuctx, regs, set->sig[0])) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + if (__copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + goto give_sigsegv; + } + + /* set up to return from userspace. If provided, use a stub already in + * userspace */ + if (ka->sa.sa_flags & SA_RESTORER) { + if (__put_user(ka->sa.sa_restorer, &frame->pretcode)) + goto give_sigsegv; + } else { + if (__put_user((void (*)(void))frame->retcode, + &frame->pretcode)) + goto give_sigsegv; + /* this is mov $,d0; syscall 0 */ + if (__put_user(0x2c, (char *)(frame->retcode + 0)) || + __put_user(__NR_sigreturn, (char *)(frame->retcode + 1)) || + __put_user(0x00, (char *)(frame->retcode + 2)) || + __put_user(0xf0, (char *)(frame->retcode + 3)) || + __put_user(0xe0, (char *)(frame->retcode + 4))) + goto give_sigsegv; + flush_icache_range((unsigned long) frame->retcode, + (unsigned long) frame->retcode + 5); + } + + /* set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->pc = (unsigned long) ka->sa.sa_handler; + regs->d0 = sig; + regs->d1 = (unsigned long) &frame->sc; + + set_fs(USER_DS); + + /* the tracer may want to single-step inside the handler */ + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + +#if DEBUG_SIG + printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, regs->pc, + frame->pretcode); +#endif + + return 0; + +give_sigsegv: + force_sig(SIGSEGV, current); + return -EFAULT; +} + +/* + * set up a realtime signal frame + */ +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe __user *frame; + int rsig; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + rsig = sig; + if (sig < 32 && + current_thread_info()->exec_domain && + current_thread_info()->exec_domain->signal_invmap) + rsig = current_thread_info()->exec_domain->signal_invmap[sig]; + + if (__put_user(rsig, &frame->sig) || + __put_user(&frame->info, &frame->pinfo) || + __put_user(&frame->uc, &frame->puc) || + copy_siginfo_to_user(&frame->info, info)) + goto give_sigsegv; + + /* create the ucontext. */ + if (__put_user(0, &frame->uc.uc_flags) || + __put_user(0, &frame->uc.uc_link) || + __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp) || + __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags) || + __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size) || + setup_sigcontext(&frame->uc.uc_mcontext, + &frame->fpuctx, regs, set->sig[0]) || + __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set))) + goto give_sigsegv; + + /* set up to return from userspace. If provided, use a stub already in + * userspace */ + if (ka->sa.sa_flags & SA_RESTORER) { + if (__put_user(ka->sa.sa_restorer, &frame->pretcode)) + goto give_sigsegv; + } else { + if (__put_user((void(*)(void))frame->retcode, + &frame->pretcode) || + /* This is mov $,d0; syscall 0 */ + __put_user(0x2c, (char *)(frame->retcode + 0)) || + __put_user(__NR_rt_sigreturn, + (char *)(frame->retcode + 1)) || + __put_user(0x00, (char *)(frame->retcode + 2)) || + __put_user(0xf0, (char *)(frame->retcode + 3)) || + __put_user(0xe0, (char *)(frame->retcode + 4))) + goto give_sigsegv; + + flush_icache_range((u_long) frame->retcode, + (u_long) frame->retcode + 5); + } + + /* Set up registers for signal handler */ + regs->sp = (unsigned long) frame; + regs->pc = (unsigned long) ka->sa.sa_handler; + regs->d0 = sig; + regs->d1 = (long) &frame->info; + + set_fs(USER_DS); + + /* the tracer may want to single-step inside the handler */ + if (test_thread_flag(TIF_SINGLESTEP)) + ptrace_notify(SIGTRAP); + +#if DEBUG_SIG + printk(KERN_DEBUG "SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, regs->pc, + frame->pretcode); +#endif + + return 0; + +give_sigsegv: + force_sig(SIGSEGV, current); + return -EFAULT; +} + +/* + * handle the actual delivery of a signal to userspace + */ +static int handle_signal(int sig, + siginfo_t *info, struct k_sigaction *ka, + sigset_t *oldset, struct pt_regs *regs) +{ + int ret; + + /* Are we from a system call? */ + if (regs->orig_d0 >= 0) { + /* If so, check system call restarting.. */ + switch (regs->d0) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + regs->d0 = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->d0 = -EINTR; + break; + } + + /* fallthrough */ + case -ERESTARTNOINTR: + regs->d0 = regs->orig_d0; + regs->pc -= 2; + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(sig, ka, info, oldset, regs); + else + ret = setup_frame(sig, ka, oldset, regs); + + if (ret == 0) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, + &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } + + return ret; +} + +/* + * handle a potential signal + */ +static void do_signal(struct pt_regs *regs) +{ + struct k_sigaction ka; + siginfo_t info; + sigset_t *oldset; + int signr; + + /* we want the common case to go fast, which is why we may in certain + * cases get here from kernel mode */ + if (!user_mode(regs)) + return; + + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { + /* a signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + + return; + } + + /* did we come from a system call? */ + if (regs->orig_d0 >= 0) { + /* restart the system call - no handlers present */ + switch (regs->d0) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: + regs->d0 = regs->orig_d0; + regs->pc -= 2; + break; + + case -ERESTART_RESTARTBLOCK: + regs->d0 = __NR_restart_syscall; + regs->pc -= 2; + break; + } + } + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); + } +} + +/* + * notification of userspace execution resumption + * - triggered by current->work.notify_resume + */ +asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) +{ + /* Pending single-step? */ + if (thread_info_flags & _TIF_SINGLESTEP) { +#ifndef CONFIG_MN10300_USING_JTAG + regs->epsw |= EPSW_T; + clear_thread_flag(TIF_SINGLESTEP); +#else + BUG(); /* no h/w single-step if using JTAG unit */ +#endif + } + + /* deal with pending signal delivery */ + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) + do_signal(regs); +} diff --git a/arch/mn10300/kernel/switch_to.S b/arch/mn10300/kernel/switch_to.S new file mode 100644 index 00000000000..630aad71b94 --- /dev/null +++ b/arch/mn10300/kernel/switch_to.S @@ -0,0 +1,71 @@ +############################################################################### +# +# MN10300 Context switch operation +# +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/thread_info.h> +#include <asm/cpu-regs.h> + + .text + +############################################################################### +# +# struct task_struct *__switch_to(struct thread_struct *prev, +# struct thread_struct *next, +# struct task_struct *prev_task) +# +############################################################################### +ENTRY(__switch_to) + movm [d2,d3,a2,a3,exreg1],(sp) + or EPSW_NMID,epsw + + mov (44,sp),d2 + + mov d0,a0 + mov d1,a1 + + # save prev context + mov (__frame),d0 + mov d0,(THREAD_FRAME,a0) + mov __switch_back,d0 + mov d0,(THREAD_PC,a0) + mov sp,a2 + mov a2,(THREAD_SP,a0) + mov a3,(THREAD_A3,a0) + + mov (THREAD_A3,a1),a3 + mov (THREAD_SP,a1),a2 + + # switch + mov a2,sp + + # load next context + GET_THREAD_INFO a2 + mov a2,(__current_ti) + mov (TI_task,a2),a2 + mov a2,(__current) +#ifdef CONFIG_MN10300_CURRENT_IN_E2 + mov a2,e2 +#endif + + mov (THREAD_FRAME,a1),a2 + mov a2,(__frame) + mov (THREAD_PC,a1),a2 + mov d2,d0 # for ret_from_fork + mov d0,a0 # for __switch_to + + jmp (a2) + +__switch_back: + and ~EPSW_NMID,epsw + ret [d2,d3,a2,a3,exreg1],32 diff --git a/arch/mn10300/kernel/sys_mn10300.c b/arch/mn10300/kernel/sys_mn10300.c new file mode 100644 index 00000000000..5f17a1ebc82 --- /dev/null +++ b/arch/mn10300/kernel/sys_mn10300.c @@ -0,0 +1,193 @@ +/* MN10300 Weird system calls + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/syscalls.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/utsname.h> +#include <linux/syscalls.h> +#include <linux/tty.h> + +#include <asm/uaccess.h> + +#define MIN_MAP_ADDR PAGE_SIZE /* minimum fixed mmap address */ + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage long sys_pipe(unsigned long __user *fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2 * sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* + * memory mapping syscall + */ +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + struct file *file = NULL; + long error = -EINVAL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + if (flags & MAP_FIXED && addr < MIN_MAP_ADDR) + goto out; + + error = -EBADF; + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage long old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset) +{ + if (offset & ~PAGE_MASK) + return -EINVAL; + return sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +} + +struct sel_arg_struct { + unsigned long n; + fd_set *inp; + fd_set *outp; + fd_set *exp; + struct timeval *tvp; +}; + +asmlinkage int old_select(struct sel_arg_struct __user *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage long sys_ipc(uint call, int first, int second, + int third, void __user *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, NULL); + case SEMTIMEDOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + second, + (const struct timespec __user *)fifth); + case SEMGET: + return sys_semget(first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void __user * __user *) ptr)) + return -EFAULT; + return sys_semctl(first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd(first, (struct msgbuf __user *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge __user *) ptr, + sizeof(tmp))) + return -EFAULT; + return sys_msgrcv(first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv(first, + (struct msgbuf __user *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget((key_t) first, second); + case MSGCTL: + return sys_msgctl(first, second, + (struct msqid_ds __user *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = do_shmat(first, (char __user *) ptr, second, + &raddr); + if (ret) + return ret; + return put_user(raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return do_shmat(first, (char __user *) ptr, second, + (ulong *) third); + } + case SHMDT: + return sys_shmdt((char __user *)ptr); + case SHMGET: + return sys_shmget(first, second, third); + case SHMCTL: + return sys_shmctl(first, second, + (struct shmid_ds __user *) ptr); + default: + return -EINVAL; + } +} diff --git a/arch/mn10300/kernel/time.c b/arch/mn10300/kernel/time.c new file mode 100644 index 00000000000..ff492e3b345 --- /dev/null +++ b/arch/mn10300/kernel/time.c @@ -0,0 +1,129 @@ +/* MN10300 Low level time management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/kernel/time.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/profile.h> +#include <asm/irq.h> +#include <asm/div64.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc.h> + +#ifdef CONFIG_MN10300_RTC +unsigned long mn10300_ioclk; /* system I/O clock frequency */ +unsigned long mn10300_iobclk; /* system I/O clock frequency */ +unsigned long mn10300_tsc_per_HZ; /* number of ioclks per jiffy */ +#endif /* CONFIG_MN10300_RTC */ + +static unsigned long mn10300_last_tsc; /* time-stamp counter at last time + * interrupt occurred */ + +static irqreturn_t timer_interrupt(int irq, void *dev_id); + +static struct irqaction timer_irq = { + .handler = timer_interrupt, + .flags = IRQF_DISABLED | IRQF_SHARED | IRQF_TIMER, + .mask = CPU_MASK_NONE, + .name = "timer", +}; + +/* + * scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + union { + unsigned long long l; + u32 w[2]; + } quot; + + quot.w[0] = mn10300_last_tsc - get_cycles(); + quot.w[1] = 1000000000; + + asm("mulu %2,%3,%0,%1" + : "=r"(quot.w[1]), "=r"(quot.w[0]) + : "0"(quot.w[1]), "1"(quot.w[0]) + : "cc"); + + do_div(quot.l, MN10300_TSCCLK); + + return quot.l; +} + +/* + * advance the kernel's time keeping clocks (xtime and jiffies) + * - we use Timer 0 & 1 cascaded as a clock to nudge us the next time + * there's a need to update + */ +static irqreturn_t timer_interrupt(int irq, void *dev_id) +{ + unsigned tsc, elapse; + + write_seqlock(&xtime_lock); + + while (tsc = get_cycles(), + elapse = mn10300_last_tsc - tsc, /* time elapsed since last + * tick */ + elapse > MN10300_TSC_PER_HZ + ) { + mn10300_last_tsc -= MN10300_TSC_PER_HZ; + + /* advance the kernel's time tracking system */ + profile_tick(CPU_PROFILING); + do_timer(1); + update_process_times(user_mode(get_irq_regs())); + check_rtc_time(); + } + + write_sequnlock(&xtime_lock); + return IRQ_HANDLED; +} + +/* + * initialise the various timers used by the main part of the kernel + */ +void __init time_init(void) +{ + /* we need the prescalar running to be able to use IOCLK/8 + * - IOCLK runs at 1/4 (ST5 open) or 1/8 (ST5 closed) internal CPU clock + * - IOCLK runs at Fosc rate (crystal speed) + */ + TMPSCNT |= TMPSCNT_ENABLE; + + startup_timestamp_counter(); + + printk(KERN_INFO + "timestamp counter I/O clock running at %lu.%02lu" + " (calibrated against RTC)\n", + MN10300_TSCCLK / 1000000, (MN10300_TSCCLK / 10000) % 100); + + xtime.tv_sec = get_initial_rtc_time(); + xtime.tv_nsec = 0; + + mn10300_last_tsc = TMTSCBC; + + /* use timer 0 & 1 cascaded to tick at as close to HZ as possible */ + setup_irq(TMJCIRQ, &timer_irq); + + set_intr_level(TMJCIRQ, TMJCICR_LEVEL); + + startup_jiffies_counter(); + +#ifdef CONFIG_MN10300_WD_TIMER + /* start the watchdog timer */ + watchdog_go(); +#endif +} diff --git a/arch/mn10300/kernel/traps.c b/arch/mn10300/kernel/traps.c new file mode 100644 index 00000000000..8b9dc6d9dcc --- /dev/null +++ b/arch/mn10300/kernel/traps.c @@ -0,0 +1,619 @@ +/* MN10300 Exception handling + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/kallsyms.h> +#include <linux/pci.h> +#include <linux/kdebug.h> +#include <linux/bug.h> +#include <linux/irq.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/cacheflush.h> +#include <asm/cpu-regs.h> +#include <asm/busctl-regs.h> +#include <asm/unit/leds.h> +#include <asm/fpu.h> +#include <asm/gdb-stub.h> +#include <asm/sections.h> + +#if (CONFIG_INTERRUPT_VECTOR_BASE & 0xffffff) +#error "INTERRUPT_VECTOR_BASE not aligned to 16MiB boundary!" +#endif + +struct pt_regs *__frame; /* current frame pointer */ +EXPORT_SYMBOL(__frame); + +int kstack_depth_to_print = 24; + +spinlock_t die_lock = __SPIN_LOCK_UNLOCKED(die_lock); + +ATOMIC_NOTIFIER_HEAD(mn10300_die_chain); + +/* + * These constants are for searching for possible module text + * segments. MODULE_RANGE is a guess of how much space is likely + * to be vmalloced. + */ +#define MODULE_RANGE (8 * 1024 * 1024) + +#define DO_ERROR(signr, prologue, str, name) \ +asmlinkage void name(struct pt_regs *regs, u32 intcode) \ +{ \ + prologue; \ + if (die_if_no_fixup(str, regs, intcode)) \ + return; \ + force_sig(signr, current); \ +} + +#define DO_EINFO(signr, prologue, str, name, sicode) \ +asmlinkage void name(struct pt_regs *regs, u32 intcode) \ +{ \ + siginfo_t info; \ + prologue; \ + if (die_if_no_fixup(str, regs, intcode)) \ + return; \ + info.si_signo = signr; \ + if (signr == SIGILL && sicode == ILL_ILLOPC) { \ + uint8_t opcode; \ + if (get_user(opcode, (uint8_t __user *)regs->pc) == 0) \ + if (opcode == 0xff) \ + info.si_signo = SIGTRAP; \ + } \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *) regs->pc; \ + force_sig_info(info.si_signo, &info, current); \ +} + +DO_ERROR(SIGTRAP, {}, "trap", trap); +DO_ERROR(SIGSEGV, {}, "ibreak", ibreak); +DO_ERROR(SIGSEGV, {}, "obreak", obreak); +DO_EINFO(SIGSEGV, {}, "access error", access_error, SEGV_ACCERR); +DO_EINFO(SIGSEGV, {}, "insn access error", insn_acc_error, SEGV_ACCERR); +DO_EINFO(SIGSEGV, {}, "data access error", data_acc_error, SEGV_ACCERR); +DO_EINFO(SIGILL, {}, "privileged opcode", priv_op, ILL_PRVOPC); +DO_EINFO(SIGILL, {}, "invalid opcode", invalid_op, ILL_ILLOPC); +DO_EINFO(SIGILL, {}, "invalid ex opcode", invalid_exop, ILL_ILLOPC); +DO_EINFO(SIGBUS, {}, "invalid address", mem_error, BUS_ADRERR); +DO_EINFO(SIGBUS, {}, "bus error", bus_error, BUS_ADRERR); +DO_EINFO(SIGILL, {}, "FPU invalid opcode", fpu_invalid_op, ILL_COPROC); + +DO_ERROR(SIGTRAP, +#ifndef CONFIG_MN10300_USING_JTAG + DCR &= ~0x0001, +#else + {}, +#endif + "single step", istep); + +/* + * handle NMI + */ +asmlinkage void nmi(struct pt_regs *regs, enum exception_code code) +{ + /* see if gdbstub wants to deal with it */ +#ifdef CONFIG_GDBSTUB + if (gdbstub_intercept(regs, code)) + return; +#endif + + printk(KERN_WARNING "--- Register Dump ---\n"); + show_registers(regs); + printk(KERN_WARNING "---------------------\n"); +} + +/* + * show a stack trace from the specified stack pointer + */ +void show_trace(unsigned long *sp) +{ + unsigned long *stack, addr, module_start, module_end; + int i; + + printk(KERN_EMERG "\n" + KERN_EMERG "Call Trace:"); + + stack = sp; + i = 0; + module_start = VMALLOC_START; + module_end = VMALLOC_END; + + while (((long) stack & (THREAD_SIZE - 1)) != 0) { + addr = *stack++; + if (__kernel_text_address(addr)) { +#if 1 + printk(" [<%08lx>]", addr); + print_symbol(" %s", addr); + printk("\n"); +#else + if ((i % 6) == 0) + printk("\n" KERN_EMERG " "); + printk("[<%08lx>] ", addr); + i++; +#endif + } + } + + printk("\n"); +} + +/* + * show the raw stack from the specified stack pointer + */ +void show_stack(struct task_struct *task, unsigned long *sp) +{ + unsigned long *stack; + int i; + + if (!sp) + sp = (unsigned long *) &sp; + + stack = sp; + printk(KERN_EMERG "Stack:"); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE - 1)) == 0) + break; + if ((i % 8) == 0) + printk("\n" KERN_EMERG " "); + printk("%08lx ", *stack++); + } + + show_trace(sp); +} + +/* + * the architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long stack; + + show_stack(current, &stack); +} +EXPORT_SYMBOL(dump_stack); + +/* + * dump the register file in the specified exception frame + */ +void show_registers_only(struct pt_regs *regs) +{ + unsigned long ssp; + + ssp = (unsigned long) regs + sizeof(*regs); + + printk(KERN_EMERG "PC: %08lx EPSW: %08lx SSP: %08lx mode: %s\n", + regs->pc, regs->epsw, ssp, user_mode(regs) ? "User" : "Super"); + printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + regs->d0, regs->d1, regs->d2, regs->d3); + printk(KERN_EMERG "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n", + regs->a0, regs->a1, regs->a2, regs->a3); + printk(KERN_EMERG "e0: %08lx e1: %08lx e2: %08lx e3: %08lx\n", + regs->e0, regs->e1, regs->e2, regs->e3); + printk(KERN_EMERG "e4: %08lx e5: %08lx e6: %08lx e7: %08lx\n", + regs->e4, regs->e5, regs->e6, regs->e7); + printk(KERN_EMERG "lar: %08lx lir: %08lx mdr: %08lx usp: %08lx\n", + regs->lar, regs->lir, regs->mdr, regs->sp); + printk(KERN_EMERG "cvf: %08lx crl: %08lx crh: %08lx drq: %08lx\n", + regs->mcvf, regs->mcrl, regs->mcrh, regs->mdrq); + printk(KERN_EMERG "threadinfo=%p task=%p)\n", + current_thread_info(), current); + + if ((unsigned long) current >= 0x90000000UL && + (unsigned long) current < 0x94000000UL) + printk(KERN_EMERG "Process %s (pid: %d)\n", + current->comm, current->pid); + + printk(KERN_EMERG "CPUP: %04hx\n", CPUP); + printk(KERN_EMERG "TBR: %08x\n", TBR); + printk(KERN_EMERG "DEAR: %08x\n", DEAR); + printk(KERN_EMERG "sISR: %08x\n", sISR); + printk(KERN_EMERG "NMICR: %04hx\n", NMICR); + printk(KERN_EMERG "BCBERR: %08x\n", BCBERR); + printk(KERN_EMERG "BCBEAR: %08x\n", BCBEAR); + printk(KERN_EMERG "MMUFCR: %08x\n", MMUFCR); + printk(KERN_EMERG "IPTEU : %08x IPTEL2: %08x\n", IPTEU, IPTEL2); + printk(KERN_EMERG "DPTEU: %08x DPTEL2: %08x\n", DPTEU, DPTEL2); +} + +/* + * dump the registers and the stack + */ +void show_registers(struct pt_regs *regs) +{ + unsigned long sp; + int i; + + show_registers_only(regs); + + if (!user_mode(regs)) + sp = (unsigned long) regs + sizeof(*regs); + else + sp = regs->sp; + + /* when in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + printk(KERN_EMERG "\n"); + show_stack(current, (unsigned long *) sp); + +#if 0 + printk(KERN_EMERG "\n" + KERN_EMERG "Code: "); + if (regs->pc < PAGE_OFFSET) + goto bad; + + for (i = 0; i < 20; i++) { + unsigned char c; + if (__get_user(c, &((unsigned char *) regs->pc)[i])) + goto bad; + printk("%02x ", c); + } +#else + i = 0; +#endif + } + + printk("\n"); + return; + +#if 0 +bad: + printk(KERN_EMERG " Bad PC value."); + break; +#endif +} + +/* + * + */ +void show_trace_task(struct task_struct *tsk) +{ + unsigned long sp = tsk->thread.sp; + + /* User space on another CPU? */ + if ((sp ^ (unsigned long) tsk) & (PAGE_MASK << 1)) + return; + + show_trace((unsigned long *) sp); +} + +/* + * note the untimely death of part of the kernel + */ +void die(const char *str, struct pt_regs *regs, enum exception_code code) +{ + console_verbose(); + spin_lock_irq(&die_lock); + printk(KERN_EMERG "\n" + KERN_EMERG "%s: %04x\n", + str, code & 0xffff); + show_registers(regs); + + if (regs->pc >= 0x02000000 && regs->pc < 0x04000000 && + (regs->epsw & (EPSW_IM | EPSW_IE)) != (EPSW_IM | EPSW_IE)) { + printk(KERN_EMERG "Exception in usermode interrupt handler\n"); + printk(KERN_EMERG "\n" + KERN_EMERG " Please connect to kernel debugger !!\n"); + asm volatile ("0: bra 0b"); + } + + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +/* + * see if there's a fixup handler we can force a jump to when an exception + * happens due to something kernel code did + */ +int die_if_no_fixup(const char *str, struct pt_regs *regs, + enum exception_code code) +{ + if (user_mode(regs)) + return 0; + + peripheral_leds_display_exception(code); + + switch (code) { + /* see if we can fixup the kernel accessing memory */ + case EXCEP_ITLBMISS: + case EXCEP_DTLBMISS: + case EXCEP_IAERROR: + case EXCEP_DAERROR: + case EXCEP_MEMERR: + case EXCEP_MISALIGN: + case EXCEP_BUSERROR: + case EXCEP_ILLDATACC: + case EXCEP_IOINSACC: + case EXCEP_PRIVINSACC: + case EXCEP_PRIVDATACC: + case EXCEP_DATINSACC: + if (fixup_exception(regs)) + return 1; + case EXCEP_UNIMPINS: + if (regs->pc && *(uint8_t *)regs->pc == 0xff) + if (notify_die(DIE_BREAKPOINT, str, regs, code, 0, 0)) + return 1; + break; + default: + break; + } + + /* see if gdbstub wants to deal with it */ +#ifdef CONFIG_GDBSTUB + if (gdbstub_intercept(regs, code)) + return 1; +#endif + + if (notify_die(DIE_GPF, str, regs, code, 0, 0)) + return 1; + + /* make the process die as the last resort */ + die(str, regs, code); +} + +/* + * handle unsupported syscall instructions (syscall 1-15) + */ +static asmlinkage void unsupported_syscall(struct pt_regs *regs, + enum exception_code code) +{ + struct task_struct *tsk = current; + siginfo_t info; + + /* catch a kernel BUG() */ + if (code == EXCEP_SYSCALL15 && !user_mode(regs)) { + if (report_bug(regs->pc, regs) == BUG_TRAP_TYPE_BUG) { +#ifdef CONFIG_GDBSTUB + __gdbstub_bug_trap(); +#endif + } + } + + regs->pc -= 2; /* syscall return addr is _after_ the instruction */ + + die_if_no_fixup("An unsupported syscall insn was used by the kernel\n", + regs, code); + + info.si_signo = SIGILL; + info.si_errno = ENOSYS; + info.si_code = ILL_ILLTRP; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGILL, &info, tsk); +} + +/* + * display the register file when the stack pointer gets clobbered + */ +asmlinkage void do_double_fault(struct pt_regs *regs) +{ + struct task_struct *tsk = current; + + strcpy(tsk->comm, "emergency tsk"); + tsk->pid = 0; + console_verbose(); + printk(KERN_EMERG "--- double fault ---\n"); + show_registers(regs); +} + +/* + * asynchronous bus error (external, usually I/O DMA) + */ +asmlinkage void io_bus_error(u32 bcberr, u32 bcbear, struct pt_regs *regs) +{ + console_verbose(); + + printk(KERN_EMERG "\n" + KERN_EMERG "Asynchronous I/O Bus Error\n" + KERN_EMERG "==========================\n"); + + if (bcberr & BCBERR_BEME) + printk(KERN_EMERG "- Multiple recorded errors\n"); + + printk(KERN_EMERG "- Faulting Buses:%s%s%s\n", + bcberr & BCBERR_BEMR_CI ? " CPU-Ins-Fetch" : "", + bcberr & BCBERR_BEMR_CD ? " CPU-Data" : "", + bcberr & BCBERR_BEMR_DMA ? " DMA" : ""); + + printk(KERN_EMERG "- %s %s access made to %s at address %08x\n", + bcberr & BCBERR_BEBST ? "Burst" : "Single", + bcberr & BCBERR_BERW ? "Read" : "Write", + bcberr & BCBERR_BESB_MON ? "Monitor Space" : + bcberr & BCBERR_BESB_IO ? "Internal CPU I/O Space" : + bcberr & BCBERR_BESB_EX ? "External I/O Bus" : + bcberr & BCBERR_BESB_OPEX ? "External Memory Bus" : + "On Chip Memory", + bcbear + ); + + printk(KERN_EMERG "- Detected by the %s\n", + bcberr&BCBERR_BESD ? "Bus Control Unit" : "Slave Bus"); + +#ifdef CONFIG_PCI +#define BRIDGEREGB(X) (*(volatile __u8 *)(0xBE040000 + (X))) +#define BRIDGEREGW(X) (*(volatile __u16 *)(0xBE040000 + (X))) +#define BRIDGEREGL(X) (*(volatile __u32 *)(0xBE040000 + (X))) + + printk(KERN_EMERG "- PCI Memory Paging Reg: %08x\n", + *(volatile __u32 *) (0xBFFFFFF4)); + printk(KERN_EMERG "- PCI Bridge Base Address 0: %08x\n", + BRIDGEREGL(PCI_BASE_ADDRESS_0)); + printk(KERN_EMERG "- PCI Bridge AMPCI Base Address: %08x\n", + BRIDGEREGL(0x48)); + printk(KERN_EMERG "- PCI Bridge Command: %04hx\n", + BRIDGEREGW(PCI_COMMAND)); + printk(KERN_EMERG "- PCI Bridge Status: %04hx\n", + BRIDGEREGW(PCI_STATUS)); + printk(KERN_EMERG "- PCI Bridge Int Status: %08hx\n", + BRIDGEREGL(0x4c)); +#endif + + printk(KERN_EMERG "\n"); + show_registers(regs); + + panic("Halted due to asynchronous I/O Bus Error\n"); +} + +/* + * handle an exception for which a handler has not yet been installed + */ +asmlinkage void uninitialised_exception(struct pt_regs *regs, + enum exception_code code) +{ + + /* see if gdbstub wants to deal with it */ +#ifdef CONFIG_GDBSTUB + if (gdbstub_intercept(regs, code)) + return; +#endif + + peripheral_leds_display_exception(code); + printk(KERN_EMERG "Uninitialised Exception 0x%04x\n", code & 0xFFFF); + show_registers(regs); + + for (;;) + continue; +} + +/* + * set an interrupt stub to jump to a handler + * ! NOTE: this does *not* flush the caches + */ +void __init __set_intr_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + + addr = (unsigned long) handler - (unsigned long) vector; + vector[0] = 0xdc; /* JMP handler */ + vector[1] = addr; + vector[2] = addr >> 8; + vector[3] = addr >> 16; + vector[4] = addr >> 24; + vector[5] = 0xcb; + vector[6] = 0xcb; + vector[7] = 0xcb; +} + +/* + * set an interrupt stub to jump to a handler + */ +void __init set_intr_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + + addr = (unsigned long) handler - (unsigned long) vector; + vector[0] = 0xdc; /* JMP handler */ + vector[1] = addr; + vector[2] = addr >> 8; + vector[3] = addr >> 16; + vector[4] = addr >> 24; + vector[5] = 0xcb; + vector[6] = 0xcb; + vector[7] = 0xcb; + + mn10300_dcache_flush_inv(); + mn10300_icache_inv(); +} + +/* + * set an interrupt stub to invoke the JTAG unit and then jump to a handler + */ +void __init set_jtag_stub(enum exception_code code, void *handler) +{ + unsigned long addr; + u8 *vector = (u8 *)(CONFIG_INTERRUPT_VECTOR_BASE + code); + + addr = (unsigned long) handler - ((unsigned long) vector + 1); + vector[0] = 0xff; /* PI to jump into JTAG debugger */ + vector[1] = 0xdc; /* jmp handler */ + vector[2] = addr; + vector[3] = addr >> 8; + vector[4] = addr >> 16; + vector[5] = addr >> 24; + vector[6] = 0xcb; + vector[7] = 0xcb; + + mn10300_dcache_flush_inv(); + flush_icache_range((unsigned long) vector, (unsigned long) vector + 8); +} + +/* + * initialise the exception table + */ +void __init trap_init(void) +{ + set_excp_vector(EXCEP_TRAP, trap); + set_excp_vector(EXCEP_ISTEP, istep); + set_excp_vector(EXCEP_IBREAK, ibreak); + set_excp_vector(EXCEP_OBREAK, obreak); + + set_excp_vector(EXCEP_PRIVINS, priv_op); + set_excp_vector(EXCEP_UNIMPINS, invalid_op); + set_excp_vector(EXCEP_UNIMPEXINS, invalid_exop); + set_excp_vector(EXCEP_MEMERR, mem_error); + set_excp_vector(EXCEP_MISALIGN, misalignment); + set_excp_vector(EXCEP_BUSERROR, bus_error); + set_excp_vector(EXCEP_ILLINSACC, insn_acc_error); + set_excp_vector(EXCEP_ILLDATACC, data_acc_error); + set_excp_vector(EXCEP_IOINSACC, insn_acc_error); + set_excp_vector(EXCEP_PRIVINSACC, insn_acc_error); + set_excp_vector(EXCEP_PRIVDATACC, data_acc_error); + set_excp_vector(EXCEP_DATINSACC, insn_acc_error); + set_excp_vector(EXCEP_FPU_DISABLED, fpu_disabled); + set_excp_vector(EXCEP_FPU_UNIMPINS, fpu_invalid_op); + set_excp_vector(EXCEP_FPU_OPERATION, fpu_exception); + + set_excp_vector(EXCEP_NMI, nmi); + + set_excp_vector(EXCEP_SYSCALL1, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL2, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL3, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL4, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL5, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL6, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL7, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL8, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL9, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL10, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL11, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL12, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL13, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL14, unsupported_syscall); + set_excp_vector(EXCEP_SYSCALL15, unsupported_syscall); +} + +/* + * determine if a program counter value is a valid bug address + */ +int is_valid_bugaddr(unsigned long pc) +{ + return pc >= PAGE_OFFSET; +} diff --git a/arch/mn10300/kernel/vmlinux.lds.S b/arch/mn10300/kernel/vmlinux.lds.S new file mode 100644 index 00000000000..a3e80f444f5 --- /dev/null +++ b/arch/mn10300/kernel/vmlinux.lds.S @@ -0,0 +1,159 @@ +/* MN10300 Main kernel linker script + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#define __VMLINUX_LDS__ +#include <asm-generic/vmlinux.lds.h> +#include <asm/thread_info.h> + +OUTPUT_FORMAT("elf32-am33lin", "elf32-am33lin", "elf32-am33lin") +OUTPUT_ARCH(mn10300) +ENTRY(_start) +jiffies = jiffies_64; +#ifndef CONFIG_MN10300_CURRENT_IN_E2 +current = __current; +#endif +SECTIONS +{ + . = CONFIG_KERNEL_TEXT_ADDRESS; + /* read-only */ + _stext = .; + _text = .; /* Text and read-only data */ + .text : { + *( + .text.head + .text + ) + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT + KPROBES_TEXT + *(.fixup) + *(.gnu.warning) + } = 0xcb + + _etext = .; /* End of text section */ + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + BUG_TABLE + + RODATA + + /* writeable */ + .data : { /* Data */ + DATA_DATA + CONSTRUCTORS + } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + /* rarely changed data like cpu maps */ + . = ALIGN(32); + .data.read_mostly : AT(ADDR(.data.read_mostly)) { + *(.data.read_mostly) + _edata = .; /* End of data section */ + } + + . = ALIGN(THREAD_SIZE); /* init_task */ + .data.init_task : { *(.data.init_task) } + + /* might get freed after init */ + . = ALIGN(4096); + .smp_locks : AT(ADDR(.smp_locks) - LOAD_OFFSET) { + __smp_locks = .; + *(.smp_locks) + __smp_locks_end = .; + } + + /* will be freed after init */ + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(16); + __setup_start = .; + .setup.init : { KEEP(*(.init.setup)) } + __setup_end = .; + + __initcall_start = .; + .initcall.init : { + INITCALLS + } + __initcall_end = .; + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + + SECURITY_INIT + . = ALIGN(4); + __alt_instructions = .; + .altinstructions : { *(.altinstructions) } + __alt_instructions_end = .; + .altinstr_replacement : { *(.altinstr_replacement) } + /* .exit.text is discard at runtime, not link time, to deal with references + from .altinstructions and .eh_frame */ + .exit.text : { *(.exit.text) } + .exit.data : { *(.exit.data) } + +#ifdef CONFIG_BLK_DEV_INITRD + . = ALIGN(4096); + __initramfs_start = .; + .init.ramfs : { *(.init.ramfs) } + __initramfs_end = .; +#endif + + . = ALIGN(32); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + . = ALIGN(4096); + __init_end = .; + /* freed after init ends here */ + + __bss_start = .; /* BSS */ + .bss : { + *(.bss.page_aligned) + *(.bss) + } + . = ALIGN(4); + __bss_stop = .; + + _end = . ; + + /* This is where the kernel creates the early boot page tables */ + . = ALIGN(4096); + pg0 = .; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exitcall.exit) + } + + STABS_DEBUG + + DWARF_DEBUG +} diff --git a/arch/mn10300/lib/Makefile b/arch/mn10300/lib/Makefile new file mode 100644 index 00000000000..fdfa9ec5b5b --- /dev/null +++ b/arch/mn10300/lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the MN10300-specific library files.. +# + +lib-y = delay.o usercopy.o checksum.o bitops.o memcpy.o memmove.o memset.o +lib-y += do_csum.o +lib-y += __ashldi3.o __ashrdi3.o __lshrdi3.o negdi2.o diff --git a/arch/mn10300/lib/__ashldi3.S b/arch/mn10300/lib/__ashldi3.S new file mode 100644 index 00000000000..a51a9506f00 --- /dev/null +++ b/arch/mn10300/lib/__ashldi3.S @@ -0,0 +1,51 @@ +/* MN10300 64-bit arithmetic left shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __ashldi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __ashldi3 + .type __ashldi3,@function +__ashldi3: + mov (12,sp),a0 + and +63,a0 + beq __ashldi3_zero + + cmp +31,a0 + bhi __ashldi3_32plus + + # the count is in the range 1-31 + asl a0,d1 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + lsr a1,d0,a1 # get overflow from LSW -> MSW + + or_asl a1,d1,a0,d0 # insert overflow into MSW and + # shift the LSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__ashldi3_32plus: + asl a0,d0,d1 + clr d0 +__ashldi3_zero: + rets + + .size __ashldi3, .-__ashldi3 diff --git a/arch/mn10300/lib/__ashrdi3.S b/arch/mn10300/lib/__ashrdi3.S new file mode 100644 index 00000000000..6f42382728c --- /dev/null +++ b/arch/mn10300/lib/__ashrdi3.S @@ -0,0 +1,52 @@ +/* MN10300 64-bit arithmetic right shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __ashrdi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __ashrdi3 + .type __ashrdi3,@function +__ashrdi3: + mov (12,sp),a0 + and +63,a0 + beq __ashrdi3_zero + + cmp +31,a0 + bhi __ashrdi3_32plus + + # the count is in the range 1-31 + lsr a0,d0 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + asl a1,d1,a1 # get underflow from MSW -> LSW + + or_asr a1,d0,a0,d1 # insert underflow into LSW and + # shift the MSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__ashrdi3_32plus: + asr a0,d1,d0 + ext d0 # sign-extend result through MDR + mov mdr,d1 +__ashrdi3_zero: + rets + + .size __ashrdi3, .-__ashrdi3 diff --git a/arch/mn10300/lib/__lshrdi3.S b/arch/mn10300/lib/__lshrdi3.S new file mode 100644 index 00000000000..a686aef31e9 --- /dev/null +++ b/arch/mn10300/lib/__lshrdi3.S @@ -0,0 +1,52 @@ +/* MN10300 64-bit logical right shift + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <asm/cache.h> + + .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned long long __lshrdi3(unsigned long long value [D1:D0], +# unsigned by [(12,SP)]) +# +############################################################################### + .globl __lshrdi3 + .type __lshrdi3,@function +__lshrdi3: + mov (12,sp),a0 + and +63,a0 + beq __lshrdi3_zero + + cmp +31,a0 + bhi __lshrdi3_32plus + + # the count is in the range 1-31 + lsr a0,d0 + + mov +32,a1 + sub a0,a1,a1 # a1 = 32 - count + asl a1,d1,a1 # get underflow from MSW -> LSW + + or_lsr a1,d0,a0,d1 # insert underflow into LSW and + # shift the MSW + rets + + .balign L1_CACHE_BYTES + # the count is in the range 32-63 +__lshrdi3_32plus: + lsr a0,d1,d0 + clr d1 +__lshrdi3_zero: + rets + + .size __lshrdi3, .-__lshrdi3 diff --git a/arch/mn10300/lib/ashrdi3.c b/arch/mn10300/lib/ashrdi3.c new file mode 100644 index 00000000000..c54f61ddf0b --- /dev/null +++ b/arch/mn10300/lib/ashrdi3.c @@ -0,0 +1,61 @@ +/* ashrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__((mode(SI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef int word_type __attribute__((mode(__word__))); + +struct DIstruct { + SItype low; + SItype high; +}; + +union DIunion { + struct DIstruct s; + DItype ll; +}; + +DItype __ashrdi3(DItype u, word_type b) +{ + union DIunion w; + union DIunion uu; + word_type bm; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = uu.s.high >> (sizeof(SItype) * BITS_PER_UNIT - 1); + w.s.low = uu.s.high >> -bm; + } else { + USItype carries = (USItype)uu.s.high << bm; + w.s.high = uu.s.high >> b; + w.s.low = ((USItype)uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/mn10300/lib/bitops.c b/arch/mn10300/lib/bitops.c new file mode 100644 index 00000000000..440a7dcbf87 --- /dev/null +++ b/arch/mn10300/lib/bitops.c @@ -0,0 +1,51 @@ +/* MN10300 Non-trivial bit operations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <asm/bitops.h> +#include <asm/system.h> + +/* + * try flipping a bit using BSET and BCLR + */ +void change_bit(int nr, volatile void *addr) +{ + if (test_bit(nr, addr)) + goto try_clear_bit; + +try_set_bit: + if (!test_and_set_bit(nr, addr)) + return; + +try_clear_bit: + if (test_and_clear_bit(nr, addr)) + return; + + goto try_set_bit; +} + +/* + * try flipping a bit using BSET and BCLR and returning the old value + */ +int test_and_change_bit(int nr, volatile void *addr) +{ + if (test_bit(nr, addr)) + goto try_clear_bit; + +try_set_bit: + if (!test_and_set_bit(nr, addr)) + return 0; + +try_clear_bit: + if (test_and_clear_bit(nr, addr)) + return 1; + + goto try_set_bit; +} diff --git a/arch/mn10300/lib/checksum.c b/arch/mn10300/lib/checksum.c new file mode 100644 index 00000000000..274f29ec33c --- /dev/null +++ b/arch/mn10300/lib/checksum.c @@ -0,0 +1,99 @@ +/* MN10300 Optimised checksumming wrappers + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/errno.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> +#include <asm/checksum.h> +#include "internal.h" + +static inline unsigned short from32to16(__wsum sum) +{ + asm(" add %1,%0 \n" + " addc 0xffff,%0 \n" + : "=r" (sum) + : "r" (sum << 16), "0" (sum & 0xffff0000) + ); + return sum >> 16; +} + +__sum16 ip_fast_csum(const void *iph, unsigned int ihl) +{ + return ~do_csum(iph, ihl * 4); +} +EXPORT_SYMBOL(ip_fast_csum); + +__wsum csum_partial(const void *buff, int len, __wsum sum) +{ + __wsum result; + + result = do_csum(buff, len); + result += sum; + if (sum > result) + result++; + return result; +} +EXPORT_SYMBOL(csum_partial); + +__sum16 ip_compute_csum(const void *buff, int len) +{ + return ~from32to16(do_csum(buff, len)); +} +EXPORT_SYMBOL(ip_compute_csum); + +__wsum csum_partial_copy(const void *src, void *dst, int len, __wsum sum) +{ + copy_from_user(dst, src, len); + return csum_partial(dst, len, sum); +} +EXPORT_SYMBOL(csum_partial_copy); + +__wsum csum_partial_copy_nocheck(const void *src, void *dst, + int len, __wsum sum) +{ + sum = csum_partial(src, len, sum); + memcpy(dst, src, len); + return sum; +} +EXPORT_SYMBOL(csum_partial_copy_nocheck); + +__wsum csum_partial_copy_from_user(const void *src, void *dst, + int len, __wsum sum, + int *err_ptr) +{ + int missing; + + missing = copy_from_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(dst, len, sum); +} +EXPORT_SYMBOL(csum_partial_copy_from_user); + +__wsum csum_and_copy_to_user(const void *src, void *dst, + int len, __wsum sum, + int *err_ptr) +{ + int missing; + + missing = copy_to_user(dst, src, len); + if (missing) { + memset(dst + len - missing, 0, missing); + *err_ptr = -EFAULT; + } + + return csum_partial(src, len, sum); +} +EXPORT_SYMBOL(csum_and_copy_to_user); diff --git a/arch/mn10300/lib/delay.c b/arch/mn10300/lib/delay.c new file mode 100644 index 00000000000..cce66bc0822 --- /dev/null +++ b/arch/mn10300/lib/delay.c @@ -0,0 +1,50 @@ +/* MN10300 Short delay interpolation routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <asm/div64.h> + +/* + * basic delay loop + */ +void __delay(unsigned long loops) +{ + int d0; + + asm volatile( + " bra 1f \n" + " .align 4 \n" + "1: bra 2f \n" + " .align 4 \n" + "2: add -1,%0 \n" + " bne 2b \n" + : "=&d" (d0) + : "0" (loops)); +} +EXPORT_SYMBOL(__delay); + +/* + * handle a delay specified in terms of microseconds + */ +void __udelay(unsigned long usecs) +{ + signed long ioclk, stop; + + /* usecs * CLK / 1E6 */ + stop = __muldiv64u(usecs, MN10300_TSCCLK, 1000000); + stop = TMTSCBC - stop; + + do { + ioclk = TMTSCBC; + } while (stop < ioclk); +} +EXPORT_SYMBOL(__udelay); diff --git a/arch/mn10300/lib/do_csum.S b/arch/mn10300/lib/do_csum.S new file mode 100644 index 00000000000..e138994e166 --- /dev/null +++ b/arch/mn10300/lib/do_csum.S @@ -0,0 +1,162 @@ +/* Optimised simple memory checksum + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# unsigned int do_csum(const unsigned char *buff, size_t len) +# +############################################################################### + .globl do_csum + .type do_csum,@function +do_csum: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov d1,d2 # count + mov d0,a0 # buff + clr d1 # accumulator + + cmp +0,d2 + beq do_csum_done # return if zero-length buffer + + # 4-byte align the buffer pointer + btst +3,a0 + beq do_csum_now_4b_aligned + + btst +1,a0 + beq do_csum_addr_not_odd + movbu (a0),d0 + inc a0 + asl +8,d0 + add d0,d1 + addc +0,d1 + add -1,d2 +do_csum_addr_not_odd: + + cmp +2,d2 + bcs do_csum_fewer_than_4 + btst +2,a0 + beq do_csum_now_4b_aligned + movhu (a0+),d0 + add d0,d1 + addc +0,d1 + add -2,d2 + cmp +4,d2 + bcs do_csum_fewer_than_4 + +do_csum_now_4b_aligned: + # we want to checksum as much as we can in chunks of 32 bytes + cmp +31,d2 + bls do_csum_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +do_csum_loop: + mov (a0+),d0 + add d0,d1 + mov (a0+),e0 + addc e0,d1 + mov (a0+),e1 + addc e1,d1 + mov (a0+),e3 + addc e3,d1 + mov (a0+),d0 + addc d0,d1 + mov (a0+),e0 + addc e0,d1 + mov (a0+),e1 + addc e1,d1 + mov (a0+),e3 + addc e3,d1 + addc +0,d1 + + sub d3,d2 + bcc do_csum_loop + + add d3,d2 + beq do_csum_done + +do_csum_remainder: + # cut 16-31 bytes down to 0-15 + cmp +16,d2 + bcs do_csum_fewer_than_16 + mov (a0+),d0 + add d0,d1 + mov (a0+),e0 + addc e0,d1 + mov (a0+),e1 + addc e1,d1 + mov (a0+),e3 + addc e3,d1 + addc +0,d1 + add -16,d2 + beq do_csum_done + +do_csum_fewer_than_16: + # copy the remaining whole words + cmp +4,d2 + bcs do_csum_fewer_than_4 + cmp +8,d2 + bcs do_csum_one_word + cmp +12,d2 + bcs do_csum_two_words + mov (a0+),d0 + add d0,d1 + addc +0,d1 +do_csum_two_words: + mov (a0+),d0 + add d0,d1 + addc +0,d1 +do_csum_one_word: + mov (a0+),d0 + add d0,d1 + addc +0,d1 + +do_csum_fewer_than_4: + and +3,d2 + beq do_csum_done + xor_cmp d0,d0,+2,d2 + bcs do_csum_fewer_than_2 + movhu (a0+),d0 +do_csum_fewer_than_2: + and +1,d2 + beq do_csum_add_last_bit + movbu (a0),d3 + add d3,d0 +do_csum_add_last_bit: + add d0,d1 + addc +0,d1 + +do_csum_done: + # compress the checksum down to 16 bits + mov +0xffff0000,d2 + and d1,d2 + asl +16,d1 + add d2,d1,d0 + addc +0xffff,d0 + lsr +16,d0 + + # flip the halves of the word result if the buffer was oddly aligned + mov (12,sp),d1 + and +1,d1 + beq do_csum_not_oddly_aligned + swaph d0,d0 # exchange bits 15:8 with 7:0 + +do_csum_not_oddly_aligned: + ret [d2,d3],8 + +do_csum_end: + .size do_csum, do_csum_end-do_csum diff --git a/arch/mn10300/lib/internal.h b/arch/mn10300/lib/internal.h new file mode 100644 index 00000000000..0014eee5f04 --- /dev/null +++ b/arch/mn10300/lib/internal.h @@ -0,0 +1,15 @@ +/* Internal definitions for the arch part of the kernel library + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +/* + * do_csum.S + */ +extern unsigned int do_csum(const unsigned char *, size_t); diff --git a/arch/mn10300/lib/lshrdi3.c b/arch/mn10300/lib/lshrdi3.c new file mode 100644 index 00000000000..e05e64e9ce9 --- /dev/null +++ b/arch/mn10300/lib/lshrdi3.c @@ -0,0 +1,60 @@ +/* lshrdi3.c extracted from gcc-2.7.2/libgcc2.c which is: */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#define BITS_PER_UNIT 8 + +typedef int SItype __attribute__((mode(SI))); +typedef unsigned int USItype __attribute__((mode(SI))); +typedef int DItype __attribute__((mode(DI))); +typedef int word_type __attribute__((mode(__word__))); + +struct DIstruct { + SItype low; + SItype high; +}; + +union DIunion { + struct DIstruct s; + DItype ll; +}; + +DItype __lshrdi3(DItype u, word_type b) +{ + union DIunion w; + word_type bm; + union DIunion uu; + + if (b == 0) + return u; + + uu.ll = u; + + bm = (sizeof(SItype) * BITS_PER_UNIT) - b; + if (bm <= 0) { + w.s.high = 0; + w.s.low = (USItype) uu.s.high >> -bm; + } else { + USItype carries = (USItype) uu.s.high << bm; + w.s.high = (USItype) uu.s.high >> b; + w.s.low = ((USItype) uu.s.low >> b) | carries; + } + + return w.ll; +} diff --git a/arch/mn10300/lib/memcpy.S b/arch/mn10300/lib/memcpy.S new file mode 100644 index 00000000000..25fb9bb2604 --- /dev/null +++ b/arch/mn10300/lib/memcpy.S @@ -0,0 +1,135 @@ +/* MN10300 Optimised simple memory to memory copy + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memcpy(void *dst, const void *src, size_t n) +# +############################################################################### + .globl memcpy + .type memcpy,@function +memcpy: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + mov d0,a0 # dst + mov d1,a1 # src + mov d0,e3 # the return value + + cmp +0,d2 + beq memcpy_done # return if zero-length copy + + # see if the three parameters are all four-byte aligned + or d0,d1,d3 + or d2,d3 + and +3,d3 + bne memcpy_1 # jump if not + + # we want to transfer as much as we can in chunks of 32 bytes + cmp +31,d2 + bls memcpy_4_remainder # 4-byte aligned remainder + + movm [exreg1],(sp) + add -32,d2 + mov +32,d3 + +memcpy_4_loop: + mov (a1+),d0 + mov (a1+),d1 + mov (a1+),e0 + mov (a1+),e1 + mov (a1+),e4 + mov (a1+),e5 + mov (a1+),e6 + mov (a1+),e7 + mov d0,(a0+) + mov d1,(a0+) + mov e0,(a0+) + mov e1,(a0+) + mov e4,(a0+) + mov e5,(a0+) + mov e6,(a0+) + mov e7,(a0+) + + sub d3,d2 + bcc memcpy_4_loop + + movm (sp),[exreg1] + add d3,d2 + beq memcpy_4_no_remainder + +memcpy_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memcpy_4_three_or_fewer_words + mov (a1+),d0 + mov (a1+),d1 + mov (a1+),e0 + mov (a1+),e1 + mov d0,(a0+) + mov d1,(a0+) + mov e0,(a0+) + mov e1,(a0+) + add -16,d2 + beq memcpy_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memcpy_4_three_or_fewer_words: + cmp +8,d2 + bcs memcpy_4_one_word + beq memcpy_4_two_words + mov (a1+),d0 + mov d0,(a0+) +memcpy_4_two_words: + mov (a1+),d0 + mov d0,(a0+) +memcpy_4_one_word: + mov (a1+),d0 + mov d0,(a0+) + +memcpy_4_no_remainder: + # check we copied the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + mov (20,sp),d1 + cmp d2,d1 + beq memcpy_done + break + break + break + +memcpy_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memcpy_1: + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu (a1),d0 + movbu d0,(a0) + add_add d3,a1,d3,a0 + lcc + + mov e3,a0 + ret [d2,d3],8 + +memcpy_end: + .size memcpy, memcpy_end-memcpy diff --git a/arch/mn10300/lib/memmove.S b/arch/mn10300/lib/memmove.S new file mode 100644 index 00000000000..20b07b62b77 --- /dev/null +++ b/arch/mn10300/lib/memmove.S @@ -0,0 +1,160 @@ +/* MN10300 Optimised simple memory to memory copy, with support for overlapping + * regions + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memmove(void *dst, const void *src, size_t n) +# +############################################################################### + .globl memmove + .type memmove,@function +memmove: + # fall back to memcpy if dst < src to work bottom up + cmp d1,d0 + bcs memmove_memcpy + + # work top down + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + add d0,d2,a0 # dst end + add d1,d2,a1 # src end + mov d0,e3 # the return value + + cmp +0,d2 + beq memmove_done # return if zero-length copy + + # see if the three parameters are all four-byte aligned + or d0,d1,d3 + or d2,d3 + and +3,d3 + bne memmove_1 # jump if not + + # we want to transfer as much as we can in chunks of 32 bytes + add -4,a1 + cmp +31,d2 + bls memmove_4_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +memmove_4_loop: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + + sub d3,d2 + bcc memmove_4_loop + + add d3,d2 + beq memmove_4_no_remainder + +memmove_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memmove_4_three_or_fewer_words + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + mov (a1),d1 + sub_sub +4,a1,+4,a0 + mov d1,(a0) + mov (a1),e0 + sub_sub +4,a1,+4,a0 + mov e0,(a0) + mov (a1),e1 + sub_sub +4,a1,+4,a0 + mov e1,(a0) + add -16,d2 + beq memmove_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memmove_4_three_or_fewer_words: + cmp +8,d2 + bcs memmove_4_one_word + beq memmove_4_two_words + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) +memmove_4_two_words: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) +memmove_4_one_word: + mov (a1),d0 + sub_sub +4,a1,+4,a0 + mov d0,(a0) + +memmove_4_no_remainder: + # check we copied the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + beq memmove_done + break + break + break + +memmove_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memmove_1: + add -1,a1 + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu (a1),d0 + sub_sub d3,a1,d3,a0 + movbu d0,(a0) + lcc + + mov e3,a0 + ret [d2,d3],8 + +memmove_memcpy: + jmp memcpy + +memmove_end: + .size memmove, memmove_end-memmove diff --git a/arch/mn10300/lib/memset.S b/arch/mn10300/lib/memset.S new file mode 100644 index 00000000000..bc02e39629b --- /dev/null +++ b/arch/mn10300/lib/memset.S @@ -0,0 +1,121 @@ +/* Optimised simple memory fill + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/cache.h> + + .section .text + .balign L1_CACHE_BYTES + +############################################################################### +# +# void *memset(void *dst, int c, size_t n) +# +############################################################################### + .globl memset + .type memset,@function +memset: + movm [d2,d3],(sp) + mov d0,(12,sp) + mov d1,(16,sp) + mov (20,sp),d2 # count + mov d0,a0 # dst + mov d0,e3 # the return value + + cmp +0,d2 + beq memset_done # return if zero-length fill + + # see if the region parameters are four-byte aligned + or d0,d2,d3 + and +3,d3 + bne memset_1 # jump if not + + extbu d1 + mov_asl d1,d3,8,d1 + or_asl d1,d3,8,d1 + or_asl d1,d3,8,d1 + or d3,d1 + + # we want to transfer as much as we can in chunks of 32 bytes + cmp +31,d2 + bls memset_4_remainder # 4-byte aligned remainder + + add -32,d2 + mov +32,d3 + +memset_4_loop: + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + + sub d3,d2 + bcc memset_4_loop + + add d3,d2 + beq memset_4_no_remainder + +memset_4_remainder: + # cut 4-7 words down to 0-3 + cmp +16,d2 + bcs memset_4_three_or_fewer_words + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + mov d1,(a0+) + add -16,d2 + beq memset_4_no_remainder + + # copy the remaining 1, 2 or 3 words +memset_4_three_or_fewer_words: + cmp +8,d2 + bcs memset_4_one_word + beq memset_4_two_words + mov d1,(a0+) +memset_4_two_words: + mov d1,(a0+) +memset_4_one_word: + mov d1,(a0+) + +memset_4_no_remainder: + # check we set the correct amount + # TODO: REMOVE CHECK + sub e3,a0,d2 + mov (20,sp),d1 + cmp d2,d1 + beq memset_done + break + break + break + +memset_done: + mov e3,a0 + ret [d2,d3],8 + + # handle misaligned copying +memset_1: + add -1,d2 + mov +1,d3 + setlb # setlb requires the next insns + # to occupy exactly 4 bytes + + sub d3,d2 + movbu d1,(a0) + inc a0 + lcc + + mov e3,a0 + ret [d2,d3],8 + +memset_end: + .size memset, memset_end-memset diff --git a/arch/mn10300/lib/negdi2.c b/arch/mn10300/lib/negdi2.c new file mode 100644 index 00000000000..eae4ecdd5f6 --- /dev/null +++ b/arch/mn10300/lib/negdi2.c @@ -0,0 +1,57 @@ +/* More subroutines needed by GCC output code on some machines. */ +/* Compile this one with gcc. */ +/* Copyright (C) 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public Licence as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +In addition to the permissions in the GNU General Public Licence, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public Licence restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public Licence for more details. + +You should have received a copy of the GNU General Public Licence +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* It is incorrect to include config.h here, because this file is being + compiled for the target, and hence definitions concerning only the host + do not apply. */ + +#include <linux/types.h> + +union DWunion { + s64 ll; + struct { + s32 low; + s32 high; + } s; +}; + +s64 __negdi2(s64 u) +{ + union DWunion w; + union DWunion uu; + + uu.ll = u; + + w.s.low = -uu.s.low; + w.s.high = -uu.s.high - ((u32) w.s.low > 0); + + return w.ll; +} diff --git a/arch/mn10300/lib/usercopy.c b/arch/mn10300/lib/usercopy.c new file mode 100644 index 00000000000..a75b203059c --- /dev/null +++ b/arch/mn10300/lib/usercopy.c @@ -0,0 +1,166 @@ +/* MN10300 Userspace accessor functions + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <asm/uaccess.h> + +unsigned long +__generic_copy_to_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __copy_user(to, from, n); + return n; +} + +unsigned long +__generic_copy_from_user(void *to, const void *from, unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) + __copy_user_zeroing(to, from, n); + return n; +} + +/* + * Copy a null terminated string from userspace. + */ +#define __do_strncpy_from_user(dst, src, count, res) \ +do { \ + int w; \ + asm volatile( \ + " mov %1,%0\n" \ + " cmp 0,%1\n" \ + " beq 2f\n" \ + "0:\n" \ + " movbu (%5),%2\n" \ + "1:\n" \ + " movbu %2,(%6)\n" \ + " inc %5\n" \ + " inc %6\n" \ + " cmp 0,%2\n" \ + " beq 2f\n" \ + " add -1,%1\n" \ + " bne 0b\n" \ + "2:\n" \ + " sub %1,%0\n" \ + "3:\n" \ + " .section .fixup,\"ax\"\n" \ + "4:\n" \ + " mov %3,%0\n" \ + " jmp 3b\n" \ + " .previous\n" \ + " .section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,4b\n" \ + " .long 1b,4b\n" \ + " .previous" \ + :"=&r"(res), "=r"(count), "=&r"(w) \ + :"i"(-EFAULT), "1"(count), "a"(src), "a"(dst) \ + :"memory"); \ +} while (0) + +long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + +long +strncpy_from_user(char *dst, const char *src, long count) +{ + long res = -EFAULT; + if (access_ok(VERIFY_READ, src, 1)) + __do_strncpy_from_user(dst, src, count, res); + return res; +} + + +/* + * Clear a userspace memory + */ +#define __do_clear_user(addr, size) \ +do { \ + int w; \ + asm volatile( \ + " cmp 0,%0\n" \ + " beq 1f\n" \ + " clr %1\n" \ + "0: movbu %1,(%3,%2)\n" \ + " inc %3\n" \ + " cmp %0,%3\n" \ + " bne 0b\n" \ + "1:\n" \ + " sub %3,%0\n" \ + "2:\n" \ + ".section .fixup,\"ax\"\n" \ + "3: jmp 2b\n" \ + ".previous\n" \ + ".section __ex_table,\"a\"\n" \ + " .balign 4\n" \ + " .long 0b,3b\n" \ + ".previous\n" \ + : "+r"(size), "=&r"(w) \ + : "a"(addr), "d"(0) \ + : "memory"); \ +} while (0) + +unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); + return n; +} + +unsigned long +clear_user(void *to, unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) + __do_clear_user(to, n); + return n; +} + +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ +long strnlen_user(const char *s, long n) +{ + unsigned long res, w; + + if (!__addr_ok(s)) + return 0; + + if (n < 0 || n + (u_long) s > current_thread_info()->addr_limit.seg) + n = current_thread_info()->addr_limit.seg - (u_long)s; + + asm volatile( + "0: cmp %4,%0\n" + " beq 2f\n" + "1: movbu (%0,%3),%1\n" + " inc %0\n" + " cmp 0,%1\n" + " beq 3f\n" + " bra 0b\n" + "2: clr %0\n" + "3:\n" + ".section .fixup,\"ax\"\n" + "4: jmp 2b\n" + ".previous\n" + ".section __ex_table,\"a\"\n" + " .balign 4\n" + " .long 1b,4b\n" + ".previous\n" + :"=d"(res), "=&r"(w) + :"0"(0), "a"(s), "r"(n) + :"memory"); + return res; +} diff --git a/arch/mn10300/mm/Makefile b/arch/mn10300/mm/Makefile new file mode 100644 index 00000000000..28b9d983db0 --- /dev/null +++ b/arch/mn10300/mm/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the MN10300-specific memory management code +# + +obj-y := \ + init.o fault.o pgtable.o extable.o tlb-mn10300.o mmu-context.o \ + misalignment.o dma-alloc.o + +ifneq ($(CONFIG_MN10300_CACHE_DISABLED),y) +obj-y += cache.o cache-mn10300.o +ifeq ($(CONFIG_MN10300_CACHE_WBACK),y) +obj-y += cache-flush-mn10300.o +endif +endif diff --git a/arch/mn10300/mm/cache-flush-mn10300.S b/arch/mn10300/mm/cache-flush-mn10300.S new file mode 100644 index 00000000000..c8ed1cbac10 --- /dev/null +++ b/arch/mn10300/mm/cache-flush-mn10300.S @@ -0,0 +1,192 @@ +/* MN10300 CPU core caching routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> + + .am33_2 + .globl mn10300_dcache_flush + .globl mn10300_dcache_flush_page + .globl mn10300_dcache_flush_range + .globl mn10300_dcache_flush_range2 + .globl mn10300_dcache_flush_inv + .globl mn10300_dcache_flush_inv_page + .globl mn10300_dcache_flush_inv_range + .globl mn10300_dcache_flush_inv_range2 + +############################################################################### +# +# void mn10300_dcache_flush(void) +# Flush the entire data cache back to RAM +# +############################################################################### + ALIGN +mn10300_dcache_flush: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_dcache_flush_end + + # read the addresses tagged in the cache's tag RAM and attempt to flush + # those addresses specifically + # - we rely on the hardware to filter out invalid tag entry addresses + mov DCACHE_TAG(0,0),a0 # dcache tag RAM access address + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries + +mn10300_dcache_flush_loop: + mov (a0),d0 + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 + or L1_CACHE_TAG_VALID,d0 # retain valid entries in the + # cache + mov d0,(a1) # conditional purge + +mn10300_dcache_flush_skip: + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_dcache_flush_loop + +mn10300_dcache_flush_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_flush_page(unsigned start) +# void mn10300_dcache_flush_range(unsigned start, unsigned end) +# void mn10300_dcache_flush_range2(unsigned start, unsigned size) +# Flush a range of addresses on a page in the dcache +# +############################################################################### + ALIGN +mn10300_dcache_flush_page: + mov PAGE_SIZE,d1 +mn10300_dcache_flush_range2: + add d0,d1 +mn10300_dcache_flush_range: + movm [d2,d3],(sp) + + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_dcache_flush_range_end + + # round start addr down + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 + + # write a request to flush all instances of an address from the cache + mov DCACHE_PURGE(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache purge control + # reg address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + + or L1_CACHE_TAG_VALID,a1 # retain valid entries in the + # cache + +mn10300_dcache_flush_range_loop: + mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line + # all ways + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 + add -1,d1 + bne mn10300_dcache_flush_range_loop + +mn10300_dcache_flush_range_end: + ret [d2,d3],8 + +############################################################################### +# +# void mn10300_dcache_flush_inv(void) +# Flush the entire data cache and invalidate all entries +# +############################################################################### + ALIGN +mn10300_dcache_flush_inv: + movhu (CHCTR),d0 + btst CHCTR_DCEN,d0 + beq mn10300_dcache_flush_inv_end + + # hit each line in the dcache with an unconditional purge + mov DCACHE_PURGE(0,0),a1 # dcache purge request address + mov L1_CACHE_NWAYS*L1_CACHE_NENTRIES,d1 # total number of entries + +mn10300_dcache_flush_inv_loop: + mov (a1),d0 # unconditional purge + + add L1_CACHE_BYTES,a1 + add -1,d1 + bne mn10300_dcache_flush_inv_loop + +mn10300_dcache_flush_inv_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_flush_inv_page(unsigned start) +# void mn10300_dcache_flush_inv_range(unsigned start, unsigned end) +# void mn10300_dcache_flush_inv_range2(unsigned start, unsigned size) +# Flush and invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN +mn10300_dcache_flush_inv_page: + mov PAGE_SIZE,d1 +mn10300_dcache_flush_inv_range2: + add d0,d1 +mn10300_dcache_flush_inv_range: + movm [d2,d3],(sp) + movhu (CHCTR),d2 + btst CHCTR_DCEN,d2 + beq mn10300_dcache_flush_inv_range_end + + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start + # addr down + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 + + # write a request to flush and invalidate all instances of an address + # from the cache + mov DCACHE_PURGE(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache purge control + # reg address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + +mn10300_dcache_flush_inv_range_loop: + mov a1,(L1_CACHE_WAYDISP*0,a0) # conditionally purge this line + # in all ways + + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + and ~L1_CACHE_WAYDISP,a0 # make sure way stay on way 0 + add -1,d1 + bne mn10300_dcache_flush_inv_range_loop + +mn10300_dcache_flush_inv_range_end: + ret [d2,d3],8 diff --git a/arch/mn10300/mm/cache-mn10300.S b/arch/mn10300/mm/cache-mn10300.S new file mode 100644 index 00000000000..e839d0aedd6 --- /dev/null +++ b/arch/mn10300/mm/cache-mn10300.S @@ -0,0 +1,289 @@ +/* MN10300 CPU core caching routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/page.h> +#include <asm/cache.h> + +#define mn10300_dcache_inv_range_intr_interval \ + +((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1) + +#if mn10300_dcache_inv_range_intr_interval > 0xff +#error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less +#endif + + .am33_2 + + .globl mn10300_icache_inv + .globl mn10300_dcache_inv + .globl mn10300_dcache_inv_range + .globl mn10300_dcache_inv_range2 + .globl mn10300_dcache_inv_page + +############################################################################### +# +# void mn10300_icache_inv(void) +# Invalidate the entire icache +# +############################################################################### + ALIGN +mn10300_icache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_ICEN,d0 + beq mn10300_icache_inv_end + + mov epsw,d1 + and ~EPSW_IE,epsw + nop + nop + + # disable the icache + and ~CHCTR_ICEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # invalidate + or CHCTR_ICINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + mov CHCTR,a0 + setlb + movhu (a0),d0 + btst CHCTR_ICBUSY,d0 + lne + + # and reenable it + and ~CHCTR_ICINV,d0 + or CHCTR_ICEN,d0 + movhu d0,(a0) + movhu (a0),d0 + + mov d1,epsw + +mn10300_icache_inv_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_inv(void) +# Invalidate the entire dcache +# +############################################################################### + ALIGN +mn10300_dcache_inv: + mov CHCTR,a0 + + movhu (a0),d0 + btst CHCTR_DCEN,d0 + beq mn10300_dcache_inv_end + + mov epsw,d1 + and ~EPSW_IE,epsw + nop + nop + + # disable the dcache + and ~CHCTR_DCEN,d0 + movhu d0,(a0) + + # and wait for it to calm down + setlb + movhu (a0),d0 + btst CHCTR_DCBUSY,d0 + lne + + # invalidate + or CHCTR_DCINV,d0 + movhu d0,(a0) + + # wait for the cache to finish + mov CHCTR,a0 + setlb + movhu (a0),d0 + btst CHCTR_DCBUSY,d0 + lne + + # and reenable it + and ~CHCTR_DCINV,d0 + or CHCTR_DCEN,d0 + movhu d0,(a0) + movhu (a0),d0 + + mov d1,epsw + +mn10300_dcache_inv_end: + ret [],0 + +############################################################################### +# +# void mn10300_dcache_inv_range(unsigned start, unsigned end) +# void mn10300_dcache_inv_range2(unsigned start, unsigned size) +# void mn10300_dcache_inv_page(unsigned start) +# Invalidate a range of addresses on a page in the dcache +# +############################################################################### + ALIGN +mn10300_dcache_inv_page: + mov PAGE_SIZE,d1 +mn10300_dcache_inv_range2: + add d0,d1 +mn10300_dcache_inv_range: + movm [d2,d3,a2],(sp) + mov CHCTR,a2 + + movhu (a2),d2 + btst CHCTR_DCEN,d2 + beq mn10300_dcache_inv_range_end + + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d0 # round start + # addr down + mov d0,a1 + + add L1_CACHE_BYTES,d1 # round end addr up + and L1_CACHE_TAG_ADDRESS|L1_CACHE_TAG_ENTRY,d1 + + clr d2 # we're going to clear tag ram + # entries + + # read the tags from the tag RAM, and if they indicate a valid dirty + # cache line then invalidate that line + mov DCACHE_TAG(0,0),a0 + mov a1,d0 + and L1_CACHE_TAG_ENTRY,d0 + add d0,a0 # starting dcache tag RAM + # access address + + sub a1,d1 + lsr L1_CACHE_SHIFT,d1 # total number of entries to + # examine + + and ~(L1_CACHE_DISPARITY-1),a1 # determine comparator base + +mn10300_dcache_inv_range_outer_loop: + # disable interrupts + mov epsw,d3 + and ~EPSW_IE,epsw + nop # note that reading CHCTR and + # AND'ing D0 occupy two delay + # slots after disabling + # interrupts + + # disable the dcache + movhu (a2),d0 + and ~CHCTR_DCEN,d0 + movhu d0,(a2) + + # and wait for it to calm down + setlb + movhu (a2),d0 + btst CHCTR_DCBUSY,d0 + lne + +mn10300_dcache_inv_range_loop: + + # process the way 0 slot + mov (L1_CACHE_WAYDISP*0,a0),d0 # read the tag in the way 0 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_0 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_0 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_0: + + # process the way 1 slot + mov (L1_CACHE_WAYDISP*1,a0),d0 # read the tag in the way 1 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_1 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_1 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_1: + + # process the way 2 slot + mov (L1_CACHE_WAYDISP*2,a0),d0 # read the tag in the way 2 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_2 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_2 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_2: + + # process the way 3 slot + mov (L1_CACHE_WAYDISP*3,a0),d0 # read the tag in the way 3 slot + btst L1_CACHE_TAG_VALID,d0 + beq mn10300_dcache_inv_range_skip_3 # jump if this cacheline is not + # valid + + xor a1,d0 + lsr 12,d0 + bne mn10300_dcache_inv_range_skip_3 # jump if not this cacheline + + mov d2,(a0) # kill the tag + +mn10300_dcache_inv_range_skip_3: + + # approx every N steps we re-enable the cache and see if there are any + # interrupts to be processed + # we also break out if we've reached the end of the loop + # (the bottom nibble of the count is zero in both cases) + add L1_CACHE_BYTES,a0 + add L1_CACHE_BYTES,a1 + add -1,d1 + btst mn10300_dcache_inv_range_intr_interval,d1 + bne mn10300_dcache_inv_range_loop + + # wait for the cache to finish what it's doing + setlb + movhu (a2),d0 + btst CHCTR_DCBUSY,d0 + lne + + # and reenable it + or CHCTR_DCEN,d0 + movhu d0,(a2) + movhu (a2),d0 + + # re-enable interrupts + # - we don't bother with delay NOPs as we'll have enough instructions + # before we disable interrupts again to give the interrupts a chance + # to happen + mov d3,epsw + + # go around again if the counter hasn't yet reached zero + add 0,d1 + bne mn10300_dcache_inv_range_outer_loop + +mn10300_dcache_inv_range_end: + ret [d2,d3,a2],12 diff --git a/arch/mn10300/mm/cache.c b/arch/mn10300/mm/cache.c new file mode 100644 index 00000000000..1b76719ec1c --- /dev/null +++ b/arch/mn10300/mm/cache.c @@ -0,0 +1,121 @@ +/* MN10300 Cache flushing routines + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/threads.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/cacheflush.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +EXPORT_SYMBOL(mn10300_icache_inv); +EXPORT_SYMBOL(mn10300_dcache_inv); +EXPORT_SYMBOL(mn10300_dcache_inv_range); +EXPORT_SYMBOL(mn10300_dcache_inv_range2); +EXPORT_SYMBOL(mn10300_dcache_inv_page); + +#ifdef CONFIG_MN10300_CACHE_WBACK +EXPORT_SYMBOL(mn10300_dcache_flush); +EXPORT_SYMBOL(mn10300_dcache_flush_inv); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_range); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_range2); +EXPORT_SYMBOL(mn10300_dcache_flush_inv_page); +EXPORT_SYMBOL(mn10300_dcache_flush_range); +EXPORT_SYMBOL(mn10300_dcache_flush_range2); +EXPORT_SYMBOL(mn10300_dcache_flush_page); +#endif + +/* + * write a page back from the dcache and invalidate the icache so that we can + * run code from it that we've just written into it + */ +void flush_icache_page(struct vm_area_struct *vma, struct page *page) +{ + mn10300_dcache_flush_page(page_to_phys(page)); + mn10300_icache_inv(); +} +EXPORT_SYMBOL(flush_icache_page); + +/* + * write some code we've just written back from the dcache and invalidate the + * icache so that we can run that code + */ +void flush_icache_range(unsigned long start, unsigned long end) +{ +#ifdef CONFIG_MN10300_CACHE_WBACK + unsigned long addr, size, off; + struct page *page; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ppte, pte; + + for (; start < end; start += size) { + /* work out how much of the page to flush */ + off = start & (PAGE_SIZE - 1); + + size = end - start; + if (size > PAGE_SIZE - off) + size = PAGE_SIZE - off; + + /* get the physical address the page is mapped to from the page + * tables */ + pgd = pgd_offset(current->mm, start); + if (!pgd || !pgd_val(*pgd)) + continue; + + pud = pud_offset(pgd, start); + if (!pud || !pud_val(*pud)) + continue; + + pmd = pmd_offset(pud, start); + if (!pmd || !pmd_val(*pmd)) + continue; + + ppte = pte_offset_map(pmd, start); + if (!ppte) + continue; + pte = *ppte; + pte_unmap(ppte); + + if (pte_none(pte)) + continue; + + page = pte_page(pte); + if (!page) + continue; + + addr = page_to_phys(page); + + /* flush the dcache and invalidate the icache coverage on that + * region */ + mn10300_dcache_flush_range2(addr + off, size); + } +#endif + + mn10300_icache_inv(); +} +EXPORT_SYMBOL(flush_icache_range); + +/* + * allow userspace to flush the instruction cache + */ +asmlinkage long sys_cacheflush(unsigned long start, unsigned long end) +{ + if (end < start) + return -EINVAL; + + flush_icache_range(start, end); + return 0; +} diff --git a/arch/mn10300/mm/dma-alloc.c b/arch/mn10300/mm/dma-alloc.c new file mode 100644 index 00000000000..f3649d8f50e --- /dev/null +++ b/arch/mn10300/mm/dma-alloc.c @@ -0,0 +1,56 @@ +/* MN10300 Dynamic DMA mapping support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from: arch/i386/kernel/pci-dma.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <asm/io.h> + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int gfp) +{ + unsigned long addr; + void *ret; + + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dev == NULL || dev->coherent_dma_mask < 0xffffffff) + gfp |= GFP_DMA; + + addr = __get_free_pages(gfp, get_order(size)); + if (!addr) + return NULL; + + /* map the coherent memory through the uncached memory window */ + ret = (void *) (addr | 0x20000000); + + /* fill the memory with obvious rubbish */ + memset((void *) addr, 0xfb, size); + + /* write back and evict all cache lines covering this region */ + mn10300_dcache_flush_inv_range2(virt_to_phys((void *) addr), PAGE_SIZE); + + *dma_handle = virt_to_bus((void *) addr); + return ret; +} +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_coherent(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + unsigned long addr = (unsigned long) vaddr & ~0x20000000; + + free_pages(addr, get_order(size)); +} +EXPORT_SYMBOL(dma_free_coherent); diff --git a/arch/mn10300/mm/extable.c b/arch/mn10300/mm/extable.c new file mode 100644 index 00000000000..25e5485ab87 --- /dev/null +++ b/arch/mn10300/mm/extable.c @@ -0,0 +1,26 @@ +/* MN10300 In-kernel exception handling + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/spinlock.h> +#include <asm/uaccess.h> + +int fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *fixup; + + fixup = search_exception_tables(regs->pc); + if (fixup) { + regs->pc = fixup->fixup; + return 1; + } + + return 0; +} diff --git a/arch/mn10300/mm/fault.c b/arch/mn10300/mm/fault.c new file mode 100644 index 00000000000..78f092ca031 --- /dev/null +++ b/arch/mn10300/mm/fault.c @@ -0,0 +1,405 @@ +/* MN10300 MMU Fault handler + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/vt_kern.h> /* For unblank_screen() */ + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgalloc.h> +#include <asm/hardirq.h> +#include <asm/gdb-stub.h> +#include <asm/cpu-regs.h> + +/* + * Unlock any spinlocks which will prevent us from getting the + * message out + */ +void bust_spinlocks(int yes) +{ + if (yes) { + oops_in_progress = 1; +#ifdef CONFIG_SMP + /* Many serial drivers do __global_cli() */ + global_irq_lock = 0; +#endif + } else { + int loglevel_save = console_loglevel; +#ifdef CONFIG_VT + unblank_screen(); +#endif + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() + * without oops_in_progress set so that printk will give klogd + * a poke. Hold onto your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console + * up */ + printk(" "); + console_loglevel = loglevel_save; + } +} + +void do_BUG(const char *file, int line) +{ + bust_spinlocks(1); + printk(KERN_EMERG "------------[ cut here ]------------\n"); + printk(KERN_EMERG "kernel BUG at %s:%d!\n", file, line); +} + +#if 0 +static void print_pagetable_entries(pgd_t *pgdir, unsigned long address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + pgd = pgdir + __pgd_offset(address); + printk(KERN_DEBUG "pgd entry %p: %016Lx\n", + pgd, (long long) pgd_val(*pgd)); + + if (!pgd_present(*pgd)) { + printk(KERN_DEBUG "... pgd not present!\n"); + return; + } + pmd = pmd_offset(pgd, address); + printk(KERN_DEBUG "pmd entry %p: %016Lx\n", + pmd, (long long)pmd_val(*pmd)); + + if (!pmd_present(*pmd)) { + printk(KERN_DEBUG "... pmd not present!\n"); + return; + } + pte = pte_offset(pmd, address); + printk(KERN_DEBUG "pte entry %p: %016Lx\n", + pte, (long long) pte_val(*pte)); + + if (!pte_present(*pte)) + printk(KERN_DEBUG "... pte not present!\n"); +} +#endif + +asmlinkage void monitor_signal(struct pt_regs *); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * fault_code: + * - LSW: either MMUFCR_IFC or MMUFCR_DFC as appropriate + * - MSW: 0 if data access, 1 if instruction access + * - bit 0: TLB miss flag + * - bit 1: initial write + * - bit 2: page invalid + * - bit 3: protection violation + * - bit 4: accessor (0=user 1=kernel) + * - bit 5: 0=read 1=write + * - bit 6-8: page protection spec + * - bit 9: illegal address + * - bit 16: 0=data 1=ins + * + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long fault_code, + unsigned long address) +{ + struct vm_area_struct *vma; + struct task_struct *tsk; + struct mm_struct *mm; + unsigned long page; + siginfo_t info; + int write, fault; + +#ifdef CONFIG_GDBSTUB + /* handle GDB stub causing a fault */ + if (gdbstub_busy) { + gdbstub_exception(regs, TBR & TBR_INT_CODE); + return; + } +#endif + +#if 0 + printk(KERN_DEBUG "--- do_page_fault(%p,%s:%04lx,%08lx)\n", + regs, + fault_code & 0x10000 ? "ins" : "data", + fault_code & 0xffff, address); +#endif + + tsk = current; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + * + * This verifies that the fault happens in kernel space + * and that the fault was a page not present (invalid) error + */ + if (address >= VMALLOC_START && address < VMALLOC_END && + (fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR && + (fault_code & MMUFCR_xFC_PGINVAL) == MMUFCR_xFC_PGINVAL + ) + goto vmalloc_fault; + + mm = tsk->mm; + info.si_code = SEGV_MAPERR; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + if (in_interrupt() || !mm) + goto no_context; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { + /* accessing the stack below the stack pointer is always a + * bug */ + if ((address & PAGE_MASK) + 2 * PAGE_SIZE < regs->sp) { +#if 0 + printk(KERN_WARNING + "[%d] ### Access below stack @%lx (sp=%lx)\n", + current->pid, address, regs->sp); + printk(KERN_WARNING + "vma [%08x - %08x]\n", + vma->vm_start, vma->vm_end); + show_registers(regs); + printk(KERN_WARNING + "[%d] ### Code: [%08lx]" + " %02x %02x %02x %02x %02x %02x %02x %02x\n", + current->pid, + regs->pc, + ((u8 *) regs->pc)[0], + ((u8 *) regs->pc)[1], + ((u8 *) regs->pc)[2], + ((u8 *) regs->pc)[3], + ((u8 *) regs->pc)[4], + ((u8 *) regs->pc)[5], + ((u8 *) regs->pc)[6], + ((u8 *) regs->pc)[7] + ); +#endif + goto bad_area; + } + } + + if (expand_stack(vma, address)) + goto bad_area; + +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + info.si_code = SEGV_ACCERR; + write = 0; + switch (fault_code & (MMUFCR_xFC_PGINVAL|MMUFCR_xFC_TYPE)) { + default: /* 3: write, present */ + case MMUFCR_xFC_TYPE_WRITE: +#ifdef TEST_VERIFY_AREA + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) + printk(KERN_DEBUG "WP fault at %08lx\n", regs->pc); +#endif + /* write to absent page */ + case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_WRITE: + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + write++; + break; + + /* read from protected page */ + case MMUFCR_xFC_TYPE_READ: + goto bad_area; + + /* read from absent page present */ + case MMUFCR_xFC_PGINVAL | MMUFCR_xFC_TYPE_READ: + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + break; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + fault = handle_mm_fault(mm, vma, address, write); + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; + BUG(); + } + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; + + up_read(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up_read(&mm->mmap_sem); + monitor_signal(regs); + + /* User mode accesses just cause a SIGSEGV */ + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + +no_context: + monitor_signal(regs); + /* Are we prepared to handle this kernel fault? */ + if (fixup_exception(regs)) + return; + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + + bust_spinlocks(1); + + if (address < PAGE_SIZE) + printk(KERN_ALERT + "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT + "Unable to handle kernel paging request"); + printk(" at virtual address %08lx\n", address); + printk(" printing pc:\n"); + printk(KERN_ALERT "%08lx\n", regs->pc); + +#ifdef CONFIG_GDBSTUB + gdbstub_intercept( + regs, fault_code & 0x00010000 ? EXCEP_IAERROR : EXCEP_DAERROR); +#endif + + page = PTBR; + page = ((unsigned long *) __va(page))[address >> 22]; + printk(KERN_ALERT "*pde = %08lx\n", page); + if (page & 1) { + page &= PAGE_MASK; + address &= 0x003ff000; + page = ((unsigned long *) __va(page))[address >> PAGE_SHIFT]; + printk(KERN_ALERT "*pte = %08lx\n", page); + } + + die("Oops", regs, fault_code); + do_exit(SIGKILL); + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + monitor_signal(regs); + printk(KERN_ALERT "VM: killing process %s\n", tsk->comm); + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_USR) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up_read(&mm->mmap_sem); + monitor_signal(regs); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, tsk); + + /* Kernel mode? Handle exceptions or die */ + if ((fault_code & MMUFCR_xFC_ACCESS) == MMUFCR_xFC_ACCESS_SR) + goto no_context; + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + * + * Do _not_ use "tsk" here. We might be inside + * an interrupt in the middle of a task switch.. + */ + int index = pgd_index(address); + pgd_t *pgd, *pgd_k; + pud_t *pud, *pud_k; + pmd_t *pmd, *pmd_k; + pte_t *pte_k; + + pgd_k = init_mm.pgd + index; + + if (!pgd_present(*pgd_k)) + goto no_context; + + pud_k = pud_offset(pgd_k, address); + if (!pud_present(*pud_k)) + goto no_context; + + pmd_k = pmd_offset(pud_k, address); + if (!pmd_present(*pmd_k)) + goto no_context; + + pgd = (pgd_t *) PTBR + index; + pud = pud_offset(pgd, address); + pmd = pmd_offset(pud, address); + set_pmd(pmd, *pmd_k); + + pte_k = pte_offset_kernel(pmd_k, address); + if (!pte_present(*pte_k)) + goto no_context; + return; + } +} diff --git a/arch/mn10300/mm/init.c b/arch/mn10300/mm/init.c new file mode 100644 index 00000000000..8c5d88c7b90 --- /dev/null +++ b/arch/mn10300/mm/init.c @@ -0,0 +1,160 @@ +/* MN10300 Memory management initialisation + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/init.h> +#include <linux/initrd.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <linux/bootmem.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/dma.h> +#include <asm/tlb.h> +#include <asm/sections.h> + +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); + +unsigned long highstart_pfn, highend_pfn; + +/* + * set up paging + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES] = {0,}; + pte_t *ppte; + int loop; + + /* main kernel space -> RAM mapping is handled as 1:1 transparent by + * the MMU */ + memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir)); + memset(kernel_vmalloc_ptes, 0, sizeof(kernel_vmalloc_ptes)); + + /* load the VMALLOC area PTE table addresses into the kernel PGD */ + ppte = kernel_vmalloc_ptes; + for (loop = VMALLOC_START / (PAGE_SIZE * PTRS_PER_PTE); + loop < VMALLOC_END / (PAGE_SIZE * PTRS_PER_PTE); + loop++ + ) { + set_pgd(swapper_pg_dir + loop, __pgd(__pa(ppte) | _PAGE_TABLE)); + ppte += PAGE_SIZE / sizeof(pte_t); + } + + /* declare the sizes of the RAM zones (only use the normal zone) */ + zones_size[ZONE_NORMAL] = + (contig_page_data.bdata->node_low_pfn) - + (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT); + + /* pass the memory from the bootmem allocator to the main allocator */ + free_area_init(zones_size); + + __flush_tlb_all(); +} + +/* + * transfer all the memory from the bootmem allocator to the runtime allocator + */ +void __init mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + int tmp; + + if (!mem_map) + BUG(); + +#define START_PFN (contig_page_data.bdata->node_boot_start >> PAGE_SHIFT) +#define MAX_LOW_PFN (contig_page_data.bdata->node_low_pfn) + + max_mapnr = num_physpages = MAX_LOW_PFN - START_PFN; + high_memory = (void *) __va(MAX_LOW_PFN * PAGE_SIZE); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + + reservedpages = 0; + for (tmp = 0; tmp < num_physpages; tmp++) + if (PageReserved(&mem_map[tmp])) + reservedpages++; + + codesize = (unsigned long) &_etext - (unsigned long) &_stext; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk(KERN_INFO + "Memory: %luk/%luk available" + " (%dk kernel code, %dk reserved, %dk data, %dk init," + " %ldk highmem)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT - 10), + max_mapnr << (PAGE_SHIFT - 10), + codesize >> 10, + reservedpages << (PAGE_SHIFT - 10), + datasize >> 10, + initsize >> 10, + (unsigned long) (totalhigh_pages << (PAGE_SHIFT - 10)) + ); +} + +/* + * + */ +void free_init_pages(char *what, unsigned long begin, unsigned long end) +{ + unsigned long addr; + + for (addr = begin; addr < end; addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + init_page_count(virt_to_page(addr)); + memset((void *) addr, 0xcc, PAGE_SIZE); + free_page(addr); + totalram_pages++; + } + printk(KERN_INFO "Freeing %s: %ldk freed\n", what, (end - begin) >> 10); +} + +/* + * recycle memory containing stuff only required for initialisation + */ +void free_initmem(void) +{ + free_init_pages("unused kernel memory", + (unsigned long) &__init_begin, + (unsigned long) &__init_end); +} + +/* + * dispose of the memory on which the initial ramdisk resided + */ +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + free_init_pages("initrd memory", start, end); +} +#endif diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c new file mode 100644 index 00000000000..32aa89dc384 --- /dev/null +++ b/arch/mn10300/mm/misalignment.c @@ -0,0 +1,661 @@ +/* MN10300 Misalignment fixup handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/smp.h> +#include <asm/pgalloc.h> +#include <asm/cpu-regs.h> +#include <asm/busctl-regs.h> +#include <asm/fpu.h> +#include <asm/gdb-stub.h> +#include <asm/asm-offsets.h> + +#if 0 +#define kdebug(FMT, ...) printk(KERN_DEBUG FMT, ##__VA_ARGS__) +#else +#define kdebug(FMT, ...) do {} while (0) +#endif + +static int misalignment_addr(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + void **_address, unsigned long **_postinc); + +static int misalignment_reg(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + unsigned long **_register); + +static inline unsigned int_log2(unsigned x) +{ + unsigned y; + asm("bsch %1,%0" : "=r"(y) : "r"(x), "0"(0)); + return y; +} +#define log2(x) int_log2(x) + +static const unsigned Dreg_index[] = { + REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 +}; + +static const unsigned Areg_index[] = { + REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2 +}; + +static const unsigned Rreg_index[] = { + REG_E0 >> 2, REG_E1 >> 2, REG_E2 >> 2, REG_E3 >> 2, + REG_E4 >> 2, REG_E5 >> 2, REG_E6 >> 2, REG_E7 >> 2, + REG_A0 >> 2, REG_A1 >> 2, REG_A2 >> 2, REG_A3 >> 2, + REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 +}; + +enum format_id { + FMT_S0, + FMT_S1, + FMT_S2, + FMT_S4, + FMT_D0, + FMT_D1, + FMT_D2, + FMT_D4, + FMT_D6, + FMT_D7, + FMT_D8, + FMT_D9, +}; + +struct { + u_int8_t opsz, dispsz; +} format_tbl[16] = { + [FMT_S0] = { 8, 0 }, + [FMT_S1] = { 8, 8 }, + [FMT_S2] = { 8, 16 }, + [FMT_S4] = { 8, 32 }, + [FMT_D0] = { 16, 0 }, + [FMT_D1] = { 16, 8 }, + [FMT_D2] = { 16, 16 }, + [FMT_D4] = { 16, 32 }, + [FMT_D6] = { 24, 0 }, + [FMT_D7] = { 24, 8 }, + [FMT_D8] = { 24, 24 }, + [FMT_D9] = { 24, 32 }, +}; + +enum value_id { + DM0, /* data reg in opcode in bits 0-1 */ + DM1, /* data reg in opcode in bits 2-3 */ + DM2, /* data reg in opcode in bits 4-5 */ + AM0, /* addr reg in opcode in bits 0-1 */ + AM1, /* addr reg in opcode in bits 2-3 */ + AM2, /* addr reg in opcode in bits 4-5 */ + RM0, /* reg in opcode in bits 0-3 */ + RM1, /* reg in opcode in bits 2-5 */ + RM2, /* reg in opcode in bits 4-7 */ + RM4, /* reg in opcode in bits 8-11 */ + RM6, /* reg in opcode in bits 12-15 */ + + RD0, /* reg in displacement in bits 0-3 */ + RD2, /* reg in displacement in bits 4-7 */ + + SP, /* stack pointer */ + + SD8, /* 8-bit signed displacement */ + SD16, /* 16-bit signed displacement */ + SD24, /* 24-bit signed displacement */ + SIMM4_2, /* 4-bit signed displacement in opcode bits 4-7 */ + SIMM8, /* 8-bit signed immediate */ + IMM24, /* 24-bit unsigned immediate */ + IMM32, /* 32-bit unsigned immediate */ + IMM32_HIGH8, /* 32-bit unsigned immediate, high 8-bits in opcode */ + + DN0 = DM0, + DN1 = DM1, + DN2 = DM2, + AN0 = AM0, + AN1 = AM1, + AN2 = AM2, + RN0 = RM0, + RN1 = RM1, + RN2 = RM2, + RN4 = RM4, + RN6 = RM6, + DI = DM1, + RI = RM2, + +}; + +struct mn10300_opcode { + const char *name; + u_int32_t opcode; + u_int32_t opmask; + unsigned exclusion; + + enum format_id format; + + unsigned cpu_mask; +#define AM33 330 + + unsigned params[2]; +#define MEM(ADDR) (0x80000000 | (ADDR)) +#define MEM2(ADDR1, ADDR2) (0x80000000 | (ADDR1) << 8 | (ADDR2)) +#define MEMINC(ADDR) (0x81000000 | (ADDR)) +#define MEMINC2(ADDR, INC) (0x81000000 | (ADDR) << 8 | (INC)) +}; + +/* LIBOPCODES EXCERPT + Assemble Matsushita MN10300 instructions. + Copyright 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public Licence as published by + the Free Software Foundation; either version 2 of the Licence, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public Licence for more details. + + You should have received a copy of the GNU General Public Licence + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +static const struct mn10300_opcode mn10300_opcodes[] = { +{ "mov", 0x60, 0xf0, 0, FMT_S0, 0, {DM1, MEM(AN0)}}, +{ "mov", 0x70, 0xf0, 0, FMT_S0, 0, {MEM(AM0), DN1}}, +{ "mov", 0xf000, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), AN1}}, +{ "mov", 0xf010, 0xfff0, 0, FMT_D0, 0, {AM1, MEM(AN0)}}, +{ "mov", 0xf300, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, +{ "mov", 0xf340, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, +{ "mov", 0xf380, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), AN2}}, +{ "mov", 0xf3c0, 0xffc0, 0, FMT_D0, 0, {AM2, MEM2(DI, AN0)}}, +{ "mov", 0xf80000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, +{ "mov", 0xf81000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, +{ "mov", 0xf82000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8,AM0), AN1}}, +{ "mov", 0xf83000, 0xfff000, 0, FMT_D1, 0, {AM1, MEM2(SD8, AN0)}}, +{ "mov", 0xf8f000, 0xfffc00, 0, FMT_D1, AM33, {MEM2(SD8, AM0), SP}}, +{ "mov", 0xf8f400, 0xfffc00, 0, FMT_D1, AM33, {SP, MEM2(SD8, AN0)}}, +{ "mov", 0xf90a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, +{ "mov", 0xf91a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, +{ "mov", 0xf96a00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, +{ "mov", 0xf97a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, +{ "mov", 0xfa000000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, +{ "mov", 0xfa100000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, +{ "mov", 0xfa200000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), AN1}}, +{ "mov", 0xfa300000, 0xfff00000, 0, FMT_D2, 0, {AM1, MEM2(SD16, AN0)}}, +{ "mov", 0xfb0a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, +{ "mov", 0xfb1a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, +{ "mov", 0xfb6a0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, +{ "mov", 0xfb7a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, +{ "mov", 0xfb8e0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "mov", 0xfb9e0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, +{ "mov", 0xfc000000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, +{ "mov", 0xfc100000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, +{ "mov", 0xfc200000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), AN1}}, +{ "mov", 0xfc300000, 0xfff00000, 0, FMT_D4, 0, {AM1, MEM2(IMM32,AN0)}}, +{ "mov", 0xfd0a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, +{ "mov", 0xfd1a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, +{ "mov", 0xfd6a0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, +{ "mov", 0xfd7a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, +{ "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "mov", 0xfe6a0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, +{ "mov", 0xfe7a0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, + +{ "movhu", 0xf060, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), DN1}}, +{ "movhu", 0xf070, 0xfff0, 0, FMT_D0, 0, {DM1, MEM(AN0)}}, +{ "movhu", 0xf480, 0xffc0, 0, FMT_D0, 0, {MEM2(DI, AM0), DN2}}, +{ "movhu", 0xf4c0, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, +{ "movhu", 0xf86000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, +{ "movhu", 0xf87000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, +{ "movhu", 0xf94a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, +{ "movhu", 0xf95a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, +{ "movhu", 0xf9ea00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, +{ "movhu", 0xf9fa00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, +{ "movhu", 0xfa600000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, +{ "movhu", 0xfa700000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, +{ "movhu", 0xfb4a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, +{ "movhu", 0xfb5a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, +{ "movhu", 0xfbce0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "movhu", 0xfbde0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, +{ "movhu", 0xfbea0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, +{ "movhu", 0xfbfa0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, +{ "movhu", 0xfc600000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, +{ "movhu", 0xfc700000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, +{ "movhu", 0xfd4a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, +{ "movhu", 0xfd5a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, +{ "movhu", 0xfdea0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, +{ "movhu", 0xfdfa0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, +{ "movhu", 0xfe4a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "movhu", 0xfe5a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "movhu", 0xfeea0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, +{ "movhu", 0xfefa0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, +{ 0, 0, 0, 0, 0, 0, {0}}, +}; + +/* + * fix up misalignment problems where possible + */ +asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) +{ + const struct exception_table_entry *fixup; + const struct mn10300_opcode *pop; + unsigned long *registers = (unsigned long *) regs; + unsigned long data, *store, *postinc; + mm_segment_t seg; + siginfo_t info; + uint32_t opcode, disp, noc, xo, xm; + uint8_t *pc, byte; + void *address; + unsigned tmp, npop; + + kdebug("MISALIGN at %lx\n", regs->pc); + + if (in_interrupt()) + die("Misalignment trap in interrupt context", regs, code); + + if (regs->epsw & EPSW_IE) + asm volatile("or %0,epsw" : : "i"(EPSW_IE)); + + seg = get_fs(); + set_fs(KERNEL_DS); + + fixup = search_exception_tables(regs->pc); + + /* first thing to do is to match the opcode */ + pc = (u_int8_t *) regs->pc; + + if (__get_user(byte, pc) != 0) + goto fetch_error; + opcode = byte; + noc = 8; + + for (pop = mn10300_opcodes; pop->name; pop++) { + npop = log2(pop->opcode | pop->opmask); + if (npop <= 0 || npop > 31) + continue; + npop = (npop + 8) & ~7; + + got_more_bits: + if (npop == noc) { + if ((opcode & pop->opmask) == pop->opcode) + goto found_opcode; + } else if (npop > noc) { + xo = pop->opcode >> (npop - noc); + xm = pop->opmask >> (npop - noc); + + if ((opcode & xm) != xo) + continue; + + /* we've got a partial match (an exact match on the + * first N bytes), so we need to get some more data */ + pc++; + if (__get_user(byte, pc) != 0) + goto fetch_error; + opcode = opcode << 8 | byte; + noc += 8; + goto got_more_bits; + } else { + /* there's already been a partial match as long as the + * complete match we're now considering, so this one + * should't match */ + continue; + } + } + + /* didn't manage to find a fixup */ + if (!user_mode(regs)) + printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n", + regs->pc, opcode); + +failed: + set_fs(seg); + if (die_if_no_fixup("misalignment error", regs, code)) + return; + + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRALN; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGBUS, &info, current); + return; + + /* error reading opcodes */ +fetch_error: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %p: fault whilst reading instruction data\n", + pc); + goto failed; + +bad_addr_mode: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx: unsupported addressing mode %x\n", + regs->pc, opcode); + goto failed; + +bad_reg_mode: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx: unsupported register mode %x\n", + regs->pc, opcode); + goto failed; + +unsupported_instruction: + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx: unsupported instruction %x (%s)\n", + regs->pc, opcode, pop->name); + goto failed; + +transfer_failed: + set_fs(seg); + if (fixup) { + regs->pc = fixup->fixup; + return; + } + if (die_if_no_fixup("misalignment fixup", regs, code)) + return; + + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = 0; + info.si_addr = (void *) regs->pc; + force_sig_info(SIGSEGV, &info, current); + return; + + /* we matched the opcode */ +found_opcode: + kdebug("MISALIGN: %lx: %x==%x { %x, %x }\n", + regs->pc, opcode, pop->opcode, pop->params[0], pop->params[1]); + + tmp = format_tbl[pop->format].opsz; + if (tmp > noc) + BUG(); /* match was less complete than it ought to have been */ + + if (tmp < noc) { + tmp = noc - tmp; + opcode >>= tmp; + pc -= tmp >> 3; + } + + /* grab the extra displacement (note it's LSB first) */ + disp = 0; + tmp = format_tbl[pop->format].dispsz >> 3; + while (tmp > 0) { + tmp--; + disp <<= 8; + + pc++; + if (__get_user(byte, pc) != 0) + goto fetch_error; + disp |= byte; + } + + set_fs(KERNEL_XDS); + if (fixup || regs->epsw & EPSW_nSL) + set_fs(seg); + + tmp = (pop->params[0] ^ pop->params[1]) & 0x80000000; + if (!tmp) { + if (!user_mode(regs)) + printk(KERN_CRIT + "MISALIGN: %lx:" + " insn not move to/from memory %x\n", + regs->pc, opcode); + goto failed; + } + + if (pop->params[0] & 0x80000000) { + /* move memory to register */ + if (!misalignment_addr(registers, pop->params[0], opcode, disp, + &address, &postinc)) + goto bad_addr_mode; + + if (!misalignment_reg(registers, pop->params[1], opcode, disp, + &store)) + goto bad_reg_mode; + + if (strcmp(pop->name, "mov") == 0) { + kdebug("FIXUP: mov (%p),DARn\n", address); + if (copy_from_user(&data, (void *) address, 4) != 0) + goto transfer_failed; + if (pop->params[0] & 0x1000000) + *postinc += 4; + } else if (strcmp(pop->name, "movhu") == 0) { + kdebug("FIXUP: movhu (%p),DARn\n", address); + data = 0; + if (copy_from_user(&data, (void *) address, 2) != 0) + goto transfer_failed; + if (pop->params[0] & 0x1000000) + *postinc += 2; + } else { + goto unsupported_instruction; + } + + *store = data; + } else { + /* move register to memory */ + if (!misalignment_reg(registers, pop->params[0], opcode, disp, + &store)) + goto bad_reg_mode; + + if (!misalignment_addr(registers, pop->params[1], opcode, disp, + &address, &postinc)) + goto bad_addr_mode; + + data = *store; + + if (strcmp(pop->name, "mov") == 0) { + kdebug("FIXUP: mov %lx,(%p)\n", data, address); + if (copy_to_user((void *) address, &data, 4) != 0) + goto transfer_failed; + if (pop->params[1] & 0x1000000) + *postinc += 4; + } else if (strcmp(pop->name, "movhu") == 0) { + kdebug("FIXUP: movhu %hx,(%p)\n", + (uint16_t) data, address); + if (copy_to_user((void *) address, &data, 2) != 0) + goto transfer_failed; + if (pop->params[1] & 0x1000000) + *postinc += 2; + } else { + goto unsupported_instruction; + } + } + + tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; + regs->pc += tmp >> 3; + + set_fs(seg); + return; +} + +/* + * determine the address that was being accessed + */ +static int misalignment_addr(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + void **_address, unsigned long **_postinc) +{ + unsigned long *postinc = NULL, address = 0, tmp; + + params &= 0x7fffffff; + + do { + switch (params & 0xff) { + case DM0: + postinc = ®isters[Dreg_index[opcode & 0x03]]; + address += *postinc; + break; + case DM1: + postinc = ®isters[Dreg_index[opcode >> 2 & 0x0c]]; + address += *postinc; + break; + case DM2: + postinc = ®isters[Dreg_index[opcode >> 4 & 0x30]]; + address += *postinc; + break; + case AM0: + postinc = ®isters[Areg_index[opcode & 0x03]]; + address += *postinc; + break; + case AM1: + postinc = ®isters[Areg_index[opcode >> 2 & 0x0c]]; + address += *postinc; + break; + case AM2: + postinc = ®isters[Areg_index[opcode >> 4 & 0x30]]; + address += *postinc; + break; + case RM0: + postinc = ®isters[Rreg_index[opcode & 0x0f]]; + address += *postinc; + break; + case RM1: + postinc = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; + address += *postinc; + break; + case RM2: + postinc = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; + address += *postinc; + break; + case RM4: + postinc = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; + address += *postinc; + break; + case RM6: + postinc = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; + address += *postinc; + break; + case RD0: + postinc = ®isters[Rreg_index[disp & 0x0f]]; + address += *postinc; + break; + case RD2: + postinc = ®isters[Rreg_index[disp >> 4 & 0x0f]]; + address += *postinc; + break; + + case SD8: + case SIMM8: + address += (int32_t) (int8_t) (disp & 0xff); + break; + case SD16: + address += (int32_t) (int16_t) (disp & 0xffff); + break; + case SD24: + tmp = disp << 8; + asm("asr 8,%0" : "=r"(tmp) : "0"(tmp)); + address += tmp; + break; + case SIMM4_2: + tmp = opcode >> 4 & 0x0f; + tmp <<= 28; + asm("asr 28,%0" : "=r"(tmp) : "0"(tmp)); + address += tmp; + break; + case IMM24: + address += disp & 0x00ffffff; + break; + case IMM32: + case IMM32_HIGH8: + address += disp; + break; + default: + return 0; + } + } while ((params >>= 8)); + + *_address = (void *) address; + *_postinc = postinc; + return 1; +} + +/* + * determine the register that is acting as source/dest + */ +static int misalignment_reg(unsigned long *registers, unsigned params, + unsigned opcode, unsigned disp, + unsigned long **_register) +{ + params &= 0x7fffffff; + + if (params & 0xffffff00) + return 0; + + switch (params & 0xff) { + case DM0: + *_register = ®isters[Dreg_index[opcode & 0x03]]; + break; + case DM1: + *_register = ®isters[Dreg_index[opcode >> 2 & 0x03]]; + break; + case DM2: + *_register = ®isters[Dreg_index[opcode >> 4 & 0x03]]; + break; + case AM0: + *_register = ®isters[Areg_index[opcode & 0x03]]; + break; + case AM1: + *_register = ®isters[Areg_index[opcode >> 2 & 0x03]]; + break; + case AM2: + *_register = ®isters[Areg_index[opcode >> 4 & 0x03]]; + break; + case RM0: + *_register = ®isters[Rreg_index[opcode & 0x0f]]; + break; + case RM1: + *_register = ®isters[Rreg_index[opcode >> 2 & 0x0f]]; + break; + case RM2: + *_register = ®isters[Rreg_index[opcode >> 4 & 0x0f]]; + break; + case RM4: + *_register = ®isters[Rreg_index[opcode >> 8 & 0x0f]]; + break; + case RM6: + *_register = ®isters[Rreg_index[opcode >> 12 & 0x0f]]; + break; + case RD0: + *_register = ®isters[Rreg_index[disp & 0x0f]]; + break; + case RD2: + *_register = ®isters[Rreg_index[disp >> 4 & 0x0f]]; + break; + case SP: + *_register = ®isters[REG_SP >> 2]; + break; + + default: + return 0; + } + + return 1; +} diff --git a/arch/mn10300/mm/mmu-context.c b/arch/mn10300/mm/mmu-context.c new file mode 100644 index 00000000000..31c9d27a75a --- /dev/null +++ b/arch/mn10300/mm/mmu-context.c @@ -0,0 +1,80 @@ +/* MN10300 MMU context allocation and management + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/mmu_context.h> +#include <asm/tlbflush.h> + +/* + * list of the MMU contexts last allocated on each CPU + */ +unsigned long mmu_context_cache[NR_CPUS] = { + [0 ... NR_CPUS - 1] = MMU_CONTEXT_FIRST_VERSION * 2 - 1, +}; + +/* + * flush the specified TLB entry + */ +void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) +{ + unsigned long pteu, cnx, flags; + + addr &= PAGE_MASK; + + /* make sure the context doesn't migrate and defend against + * interference from vmalloc'd regions */ + local_irq_save(flags); + + cnx = mm_context(vma->vm_mm); + + if (cnx != MMU_NO_CONTEXT) { + pteu = addr | (cnx & 0x000000ffUL); + IPTEU = pteu; + DPTEU = pteu; + if (IPTEL & xPTEL_V) + IPTEL = 0; + if (DPTEL & xPTEL_V) + DPTEL = 0; + } + + local_irq_restore(flags); +} + +/* + * preemptively set a TLB entry + */ +void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte) +{ + unsigned long pteu, ptel, cnx, flags; + + addr &= PAGE_MASK; + ptel = pte_val(pte) & ~(xPTEL_UNUSED1 | xPTEL_UNUSED2); + + /* make sure the context doesn't migrate and defend against + * interference from vmalloc'd regions */ + local_irq_save(flags); + + cnx = mm_context(vma->vm_mm); + + if (cnx != MMU_NO_CONTEXT) { + pteu = addr | (cnx & 0x000000ffUL); + if (!(pte_val(pte) & _PAGE_NX)) { + IPTEU = pteu; + if (IPTEL & xPTEL_V) + IPTEL = ptel; + } + DPTEU = pteu; + if (DPTEL & xPTEL_V) + DPTEL = ptel; + } + + local_irq_restore(flags); +} diff --git a/arch/mn10300/mm/pgtable.c b/arch/mn10300/mm/pgtable.c new file mode 100644 index 00000000000..a477038752b --- /dev/null +++ b/arch/mn10300/mm/pgtable.c @@ -0,0 +1,197 @@ +/* MN10300 Page table management + * + * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Modified by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/highmem.h> +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <linux/spinlock.h> +#include <linux/quicklist.h> + +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/tlb.h> +#include <asm/tlbflush.h> + +void show_mem(void) +{ + unsigned long i; + int free = 0, total = 0, reserved = 0, shared = 0; + + int cached = 0; + printk(KERN_INFO "Mem-info:\n"); + show_free_areas(); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map + i)) + reserved++; + else if (PageSwapCache(mem_map + i)) + cached++; + else if (!page_count(mem_map + i)) + free++; + else + shared += page_count(mem_map + i) - 1; + } + printk(KERN_INFO "%d pages of RAM\n", total); + printk(KERN_INFO "%d free pages\n", free); + printk(KERN_INFO "%d reserved pages\n", reserved); + printk(KERN_INFO "%d pages shared\n", shared); + printk(KERN_INFO "%d pages swap cached\n", cached); +} + +/* + * Associate a large virtual page frame with a given physical page frame + * and protection flags for that frame. pfn is for the base of the page, + * vaddr is what the page gets mapped to - both must be properly aligned. + * The pmd must already be instantiated. Assumes PAE mode. + */ +void set_pmd_pfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + + if (vaddr & (PMD_SIZE-1)) { /* vaddr is misaligned */ + printk(KERN_ERR "set_pmd_pfn: vaddr misaligned\n"); + return; /* BUG(); */ + } + if (pfn & (PTRS_PER_PTE-1)) { /* pfn is misaligned */ + printk(KERN_ERR "set_pmd_pfn: pfn misaligned\n"); + return; /* BUG(); */ + } + pgd = swapper_pg_dir + pgd_index(vaddr); + if (pgd_none(*pgd)) { + printk(KERN_ERR "set_pmd_pfn: pgd_none\n"); + return; /* BUG(); */ + } + pud = pud_offset(pgd, vaddr); + pmd = pmd_offset(pud, vaddr); + set_pmd(pmd, pfn_pmd(pfn, flags)); + /* + * It's enough to flush this one mapping. + * (PGE mappings get flushed as well) + */ + __flush_tlb_one(vaddr); +} + +pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +{ + pte_t *pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT); + if (pte) + clear_page(pte); + return pte; +} + +struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +{ + struct page *pte; + +#ifdef CONFIG_HIGHPTE + pte = alloc_pages(GFP_KERNEL|__GFP_HIGHMEM|__GFP_REPEAT, 0); +#else + pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT, 0); +#endif + if (pte) + clear_highpage(pte); + return pte; +} + +/* + * List of all pgd's needed for non-PAE so it can invalidate entries + * in both cached and uncached pgd's; not needed for PAE since the + * kernel pmd is shared. If PAE were not to share the pmd a similar + * tactic would be needed. This is essentially codepath-based locking + * against pageattr.c; it is the unique case in which a valid change + * of kernel pagetables can't be lazily synchronized by vmalloc faults. + * vmalloc faults work because attached pagetables are never freed. + * If the locking proves to be non-performant, a ticketing scheme with + * checks at dup_mmap(), exec(), and other mmlist addition points + * could be used. The locking scheme was chosen on the basis of + * manfred's recommendations and having no core impact whatsoever. + * -- wli + */ +DEFINE_SPINLOCK(pgd_lock); +struct page *pgd_list; + +static inline void pgd_list_add(pgd_t *pgd) +{ + struct page *page = virt_to_page(pgd); + page->index = (unsigned long) pgd_list; + if (pgd_list) + set_page_private(pgd_list, (unsigned long) &page->index); + pgd_list = page; + set_page_private(page, (unsigned long) &pgd_list); +} + +static inline void pgd_list_del(pgd_t *pgd) +{ + struct page *next, **pprev, *page = virt_to_page(pgd); + next = (struct page *) page->index; + pprev = (struct page **) page_private(page); + *pprev = next; + if (next) + set_page_private(next, (unsigned long) pprev); +} + +void pgd_ctor(void *pgd) +{ + unsigned long flags; + + if (PTRS_PER_PMD == 1) + spin_lock_irqsave(&pgd_lock, flags); + + memcpy((pgd_t *)pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + + if (PTRS_PER_PMD > 1) + return; + + pgd_list_add(pgd); + spin_unlock_irqrestore(&pgd_lock, flags); + memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); +} + +/* never called when PTRS_PER_PMD > 1 */ +void pgd_dtor(void *pgd) +{ + unsigned long flags; /* can be called from interrupt context */ + + spin_lock_irqsave(&pgd_lock, flags); + pgd_list_del(pgd); + spin_unlock_irqrestore(&pgd_lock, flags); +} + +pgd_t *pgd_alloc(struct mm_struct *mm) +{ + return quicklist_alloc(0, GFP_KERNEL, pgd_ctor); +} + +void pgd_free(struct mm_struct *mm, pgd_t *pgd) +{ + quicklist_free(0, pgd_dtor, pgd); +} + +void __init pgtable_cache_init(void) +{ +} + +void check_pgt_cache(void) +{ + quicklist_trim(0, pgd_dtor, 25, 16); +} diff --git a/arch/mn10300/mm/tlb-mn10300.S b/arch/mn10300/mm/tlb-mn10300.S new file mode 100644 index 00000000000..789208094e9 --- /dev/null +++ b/arch/mn10300/mm/tlb-mn10300.S @@ -0,0 +1,207 @@ +############################################################################### +# +# TLB loading functions +# +# Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd. +# Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. +# Modified by David Howells (dhowells@redhat.com) +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public Licence +# as published by the Free Software Foundation; either version +# 2 of the Licence, or (at your option) any later version. +# +############################################################################### +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/smp.h> +#include <asm/intctl-regs.h> +#include <asm/frame.inc> +#include <asm/page.h> +#include <asm/pgtable.h> + +############################################################################### +# +# Instruction TLB Miss handler entry point +# +############################################################################### + .type itlb_miss,@function +ENTRY(itlb_miss) + and ~EPSW_NMID,epsw +#ifdef CONFIG_GDBSTUB + movm [d2,d3,a2],(sp) +#else + or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate + # register bank + nop + nop + nop +#endif + + mov (IPTEU),d3 + mov (PTBR),a2 + mov d3,d2 + and 0xffc00000,d2 + lsr 20,d2 + mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] + btst _PAGE_VALID,a2 + beq itlb_miss_fault # jump if doesn't point anywhere + + and ~(PAGE_SIZE-1),a2 + mov d3,d2 + and 0x003ff000,d2 + lsr 10,d2 + add d2,a2 + mov (a2),d2 # get pte from PTD[addr 21..12] + btst _PAGE_VALID,d2 + beq itlb_miss_fault # jump if doesn't point to a page + # (might be a swap id) + bset _PAGE_ACCESSED,(0,a2) + and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2 +itlb_miss_set: + mov d2,(IPTEL) # change the TLB +#ifdef CONFIG_GDBSTUB + movm (sp),[d2,d3,a2] +#endif + rti + +itlb_miss_fault: + mov _PAGE_VALID,d2 # force address error handler to be + # invoked + bra itlb_miss_set + + .size itlb_miss, . - itlb_miss + +############################################################################### +# +# Data TLB Miss handler entry point +# +############################################################################### + .type dtlb_miss,@function +ENTRY(dtlb_miss) + and ~EPSW_NMID,epsw +#ifdef CONFIG_GDBSTUB + movm [d2,d3,a2],(sp) +#else + or EPSW_nAR,epsw # switch D0-D3 & A0-A3 to the alternate + # register bank + nop + nop + nop +#endif + + mov (DPTEU),d3 + mov (PTBR),a2 + mov d3,d2 + and 0xffc00000,d2 + lsr 20,d2 + mov (a2,d2),a2 # PTD *ptd = PGD[addr 31..22] + btst _PAGE_VALID,a2 + beq dtlb_miss_fault # jump if doesn't point anywhere + + and ~(PAGE_SIZE-1),a2 + mov d3,d2 + and 0x003ff000,d2 + lsr 10,d2 + add d2,a2 + mov (a2),d2 # get pte from PTD[addr 21..12] + btst _PAGE_VALID,d2 + beq dtlb_miss_fault # jump if doesn't point to a page + # (might be a swap id) + bset _PAGE_ACCESSED,(0,a2) + and ~(xPTEL_UNUSED1|xPTEL_UNUSED2),d2 +dtlb_miss_set: + mov d2,(DPTEL) # change the TLB +#ifdef CONFIG_GDBSTUB + movm (sp),[d2,d3,a2] +#endif + rti + +dtlb_miss_fault: + mov _PAGE_VALID,d2 # force address error handler to be + # invoked + bra dtlb_miss_set + .size dtlb_miss, . - dtlb_miss + +############################################################################### +# +# Instruction TLB Address Error handler entry point +# +############################################################################### + .type itlb_aerror,@function +ENTRY(itlb_aerror) + and ~EPSW_NMID,epsw + add -4,sp + SAVE_ALL + add -4,sp # need to pass three params + + # calculate the fault code + movhu (MMUFCR_IFC),d1 + or 0x00010000,d1 # it's an instruction fetch + + # determine the page address + mov (IPTEU),a2 + mov a2,d0 + and PAGE_MASK,d0 + mov d0,(12,sp) + + clr d0 + mov d0,(IPTEL) + + and ~EPSW_NMID,epsw + or EPSW_IE,epsw + mov fp,d0 + call do_page_fault[],0 # do_page_fault(regs,code,addr + + jmp ret_from_exception + .size itlb_aerror, . - itlb_aerror + +############################################################################### +# +# Data TLB Address Error handler entry point +# +############################################################################### + .type dtlb_aerror,@function +ENTRY(dtlb_aerror) + and ~EPSW_NMID,epsw + add -4,sp + mov d1,(sp) + + movhu (MMUFCR_DFC),d1 # is it the initial valid write + # to this page? + and MMUFCR_xFC_INITWR,d1 + beq dtlb_pagefault # jump if not + + mov (DPTEL),d1 # set the dirty bit + # (don't replace with BSET!) + or _PAGE_DIRTY,d1 + mov d1,(DPTEL) + mov (sp),d1 + add 4,sp + rti + + ALIGN +dtlb_pagefault: + mov (sp),d1 + SAVE_ALL + add -4,sp # need to pass three params + + # calculate the fault code + movhu (MMUFCR_DFC),d1 + + # determine the page address + mov (DPTEU),a2 + mov a2,d0 + and PAGE_MASK,d0 + mov d0,(12,sp) + + clr d0 + mov d0,(DPTEL) + + and ~EPSW_NMID,epsw + or EPSW_IE,epsw + mov fp,d0 + call do_page_fault[],0 # do_page_fault(regs,code,addr + + jmp ret_from_exception + .size dtlb_aerror, . - dtlb_aerror diff --git a/arch/mn10300/oprofile/Kconfig b/arch/mn10300/oprofile/Kconfig new file mode 100644 index 00000000000..19d37730b66 --- /dev/null +++ b/arch/mn10300/oprofile/Kconfig @@ -0,0 +1,23 @@ + +menu "Profiling support" + depends on EXPERIMENTAL + +config PROFILING + bool "Profiling support (EXPERIMENTAL)" + help + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + + +config OPROFILE + tristate "OProfile system profiling (EXPERIMENTAL)" + depends on PROFILING + help + OProfile is a profiling system capable of profiling the + whole system, include the kernel, kernel modules, libraries, + and applications. + + If unsure, say N. + +endmenu + diff --git a/arch/mn10300/oprofile/Makefile b/arch/mn10300/oprofile/Makefile new file mode 100644 index 00000000000..918dbe60ebb --- /dev/null +++ b/arch/mn10300/oprofile/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the MN10300-specific profiling code +# +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) op_model_null.o + diff --git a/arch/mn10300/oprofile/op_model_null.c b/arch/mn10300/oprofile/op_model_null.c new file mode 100644 index 00000000000..cd4ab374bc4 --- /dev/null +++ b/arch/mn10300/oprofile/op_model_null.c @@ -0,0 +1,22 @@ +/* Null profiling driver + * + * Copyright (C) 2003 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * Licence. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/kernel.h> +#include <linux/oprofile.h> +#include <linux/init.h> +#include <linux/errno.h> + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + return -ENODEV; +} + +void oprofile_arch_exit(void) +{ +} + diff --git a/arch/mn10300/proc-mn103e010/Makefile b/arch/mn10300/proc-mn103e010/Makefile new file mode 100644 index 00000000000..ac2c9784cd2 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the MN103E010 processor chip specific code +# +obj-y := proc-init.o + diff --git a/arch/mn10300/proc-mn103e010/proc-init.c b/arch/mn10300/proc-mn103e010/proc-init.c new file mode 100644 index 00000000000..9a482efafa8 --- /dev/null +++ b/arch/mn10300/proc-mn103e010/proc-init.c @@ -0,0 +1,75 @@ +/* MN103E010 Processor initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <asm/rtc.h> + +/* + * initialise the on-silicon processor peripherals + */ +asmlinkage void __init processor_init(void) +{ + int loop; + + /* set up the exception table first */ + for (loop = 0x000; loop < 0x400; loop += 8) + __set_intr_stub(loop, __common_exception); + + __set_intr_stub(EXCEP_ITLBMISS, itlb_miss); + __set_intr_stub(EXCEP_DTLBMISS, dtlb_miss); + __set_intr_stub(EXCEP_IAERROR, itlb_aerror); + __set_intr_stub(EXCEP_DAERROR, dtlb_aerror); + __set_intr_stub(EXCEP_BUSERROR, raw_bus_error); + __set_intr_stub(EXCEP_DOUBLE_FAULT, double_fault); + __set_intr_stub(EXCEP_SYSCALL0, system_call); + + __set_intr_stub(EXCEP_NMI, nmi_handler); + __set_intr_stub(EXCEP_WDT, nmi_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL0, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL1, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL2, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL3, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL4, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL5, irq_handler); + __set_intr_stub(EXCEP_IRQ_LEVEL6, irq_handler); + + IVAR0 = EXCEP_IRQ_LEVEL0; + IVAR1 = EXCEP_IRQ_LEVEL1; + IVAR2 = EXCEP_IRQ_LEVEL2; + IVAR3 = EXCEP_IRQ_LEVEL3; + IVAR4 = EXCEP_IRQ_LEVEL4; + IVAR5 = EXCEP_IRQ_LEVEL5; + IVAR6 = EXCEP_IRQ_LEVEL6; + + mn10300_dcache_flush_inv(); + mn10300_icache_inv(); + + /* disable all interrupts and set to priority 6 (lowest) */ + for (loop = 0; loop < NR_IRQS; loop++) + GxICR(loop) = GxICR_LEVEL_6 | GxICR_DETECT; + + /* clear the timers */ + TM0MD = 0; + TM1MD = 0; + TM2MD = 0; + TM3MD = 0; + TM4MD = 0; + TM5MD = 0; + TM6MD = 0; + TM6MDA = 0; + TM6MDB = 0; + TM7MD = 0; + TM8MD = 0; + TM9MD = 0; + TM10MD = 0; + TM11MD = 0; + + calibrate_clock(); +} diff --git a/arch/mn10300/unit-asb2303/Makefile b/arch/mn10300/unit-asb2303/Makefile new file mode 100644 index 00000000000..38a5bb43b0b --- /dev/null +++ b/arch/mn10300/unit-asb2303/Makefile @@ -0,0 +1,6 @@ +############################################################################### +# +# Makefile for the ASB2303 board +# +############################################################################### +obj-y := unit-init.o smc91111.o flash.o leds.o diff --git a/arch/mn10300/unit-asb2303/flash.c b/arch/mn10300/unit-asb2303/flash.c new file mode 100644 index 00000000000..17fe083fcb6 --- /dev/null +++ b/arch/mn10300/unit-asb2303/flash.c @@ -0,0 +1,100 @@ +/* Handle mapping of the flash on the ASB2303 board + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> + +#define ASB2303_PROM_ADDR 0xA0000000 /* Boot PROM */ +#define ASB2303_PROM_SIZE (2 * 1024 * 1024) +#define ASB2303_FLASH_ADDR 0xA4000000 /* System Flash */ +#define ASB2303_FLASH_SIZE (32 * 1024 * 1024) +#define ASB2303_CONFIG_ADDR 0xA6000000 /* System Config EEPROM */ +#define ASB2303_CONFIG_SIZE (8 * 1024) + +/* + * default MTD partition table for both main flash devices, expected to be + * overridden by RedBoot + */ +static struct mtd_partition asb2303_partitions[] = { + { + .name = "Bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_CAP_ROM /* force read-only */ + }, { + .name = "Kernel", + .size = 0x00400000, + .offset = 0x00040000, + }, { + .name = "Filesystem", + .size = MTDPART_SIZ_FULL, + .offset = 0x00440000 + } +}; + +/* + * the ASB2303 Boot PROM definition + */ +static struct physmap_flash_data asb2303_bootprom_data = { + .width = 2, + .nr_parts = 1, + .parts = asb2303_partitions, +}; + +static struct resource asb2303_bootprom_resource = { + .start = ASB2303_PROM_ADDR, + .end = ASB2303_PROM_ADDR + ASB2303_PROM_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device asb2303_bootprom = { + .name = "physmap-flash", + .id = 0, + .dev.platform_data = &asb2303_bootprom_data, + .num_resources = 1, + .resource = &asb2303_bootprom_resource, +}; + +/* + * the ASB2303 System Flash definition + */ +static struct physmap_flash_data asb2303_sysflash_data = { + .width = 4, + .nr_parts = 1, + .parts = asb2303_partitions, +}; + +static struct resource asb2303_sysflash_resource = { + .start = ASB2303_FLASH_ADDR, + .end = ASB2303_FLASH_ADDR + ASB2303_FLASH_SIZE, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device asb2303_sysflash = { + .name = "physmap-flash", + .id = 1, + .dev.platform_data = &asb2303_sysflash_data, + .num_resources = 1, + .resource = &asb2303_sysflash_resource, +}; + +/* + * register the ASB2303 flashes + */ +static int __init asb2303_mtd_init(void) +{ + platform_device_register(&asb2303_bootprom); + platform_device_register(&asb2303_sysflash); + return 0; +} + +module_init(asb2303_mtd_init); diff --git a/arch/mn10300/unit-asb2303/leds.c b/arch/mn10300/unit-asb2303/leds.c new file mode 100644 index 00000000000..cd4bc78ccfc --- /dev/null +++ b/arch/mn10300/unit-asb2303/leds.c @@ -0,0 +1,52 @@ +/* ASB2303 peripheral 7-segment LEDs x1 support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/rtc-regs.h> +#include <asm/unit/leds.h> + +#if 0 +static const u8 asb2303_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; +#endif + +static const u8 asb2303_led_chase_tbl[6] = { + ~0x02, /* top - segA */ + ~0x04, /* right top - segB */ + ~0x08, /* right bottom - segC */ + ~0x10, /* bottom - segD */ + ~0x20, /* left bottom - segE */ + ~0x40, /* left top - segF */ +}; + +static unsigned asb2303_led_chase; + +void peripheral_leds_display_exception(enum exception_code code) +{ + ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */ + ASB2303_7SEGLEDS = 0x6d; /* triple horizontal bar */ +} + +void peripheral_leds_led_chase(void) +{ + ASB2303_GPIO0DEF = 0x5555; /* configure as an output port */ + ASB2303_7SEGLEDS = asb2303_led_chase_tbl[asb2303_led_chase]; + asb2303_led_chase++; + if (asb2303_led_chase >= 6) + asb2303_led_chase = 0; +} diff --git a/arch/mn10300/unit-asb2303/smc91111.c b/arch/mn10300/unit-asb2303/smc91111.c new file mode 100644 index 00000000000..30875dd6563 --- /dev/null +++ b/arch/mn10300/unit-asb2303/smc91111.c @@ -0,0 +1,52 @@ +/* ASB2303 initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include <asm/timex.h> +#include <asm/processor.h> +#include <asm/intctl-regs.h> +#include <asm/unit/smc91111.h> + +static struct resource smc91c111_resources[] = { + [0] = { + .start = SMC91111_BASE, + .end = SMC91111_BASE_END, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = SMC91111_IRQ, + .end = SMC91111_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91c111_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91c111_resources), + .resource = smc91c111_resources, +}; + +/* + * add platform devices + */ +static int __init unit_device_init(void) +{ + platform_device_register(&smc91c111_device); + return 0; +} + +device_initcall(unit_device_init); diff --git a/arch/mn10300/unit-asb2303/unit-init.c b/arch/mn10300/unit-asb2303/unit-init.c new file mode 100644 index 00000000000..14b2c817cff --- /dev/null +++ b/arch/mn10300/unit-asb2303/unit-init.c @@ -0,0 +1,60 @@ +/* ASB2303 initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/device.h> + +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/intctl-regs.h> + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ + /* set up the external interrupts */ + SET_XIRQ_TRIGGER(0, XIRQ_TRIGGER_HILEVEL); + SET_XIRQ_TRIGGER(2, XIRQ_TRIGGER_LOWLEVEL); + SET_XIRQ_TRIGGER(3, XIRQ_TRIGGER_HILEVEL); + SET_XIRQ_TRIGGER(4, XIRQ_TRIGGER_LOWLEVEL); + SET_XIRQ_TRIGGER(5, XIRQ_TRIGGER_LOWLEVEL); +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +void __init unit_setup(void) +{ +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0; extnum < NR_XIRQS; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + set_irq_handler(XIRQ2IRQ(extnum), handle_level_irq); + break; + default: + break; + } + } +} diff --git a/arch/mn10300/unit-asb2305/Makefile b/arch/mn10300/unit-asb2305/Makefile new file mode 100644 index 00000000000..0551022225b --- /dev/null +++ b/arch/mn10300/unit-asb2305/Makefile @@ -0,0 +1,8 @@ +############################################################################### +# +# Makefile for the ASB2305 board +# +############################################################################### +obj-y := unit-init.o leds.o + +obj-$(CONFIG_PCI) += pci.o pci-asb2305.o pci-irq.o pci-iomap.o diff --git a/arch/mn10300/unit-asb2305/leds.c b/arch/mn10300/unit-asb2305/leds.c new file mode 100644 index 00000000000..e99dcc9cee1 --- /dev/null +++ b/arch/mn10300/unit-asb2305/leds.c @@ -0,0 +1,124 @@ +/* ASB2305 Peripheral 7-segment LEDs x4 support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/cpu/intctl-regs.h> +#include <asm/cpu/rtc-regs.h> +#include <asm/unit/leds.h> + +static const u8 asb2305_led_hex_tbl[16] = { + 0x80, 0xf2, 0x48, 0x60, 0x32, 0x24, 0x04, 0xf0, + 0x00, 0x20, 0x10, 0x06, 0x8c, 0x42, 0x0c, 0x1c +}; + +static const u32 asb2305_led_chase_tbl[6] = { + ~0x02020202, /* top - segA */ + ~0x04040404, /* right top - segB */ + ~0x08080808, /* right bottom - segC */ + ~0x10101010, /* bottom - segD */ + ~0x20202020, /* left bottom - segE */ + ~0x40404040, /* left top - segF */ +}; + +static unsigned asb2305_led_chase; + +void peripheral_leds7x4_display_dec(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_hex(unsigned int val, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(val/1000) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/100) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(val/10) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[val % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds_display_exception(enum exception_code code) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(code/0x100) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(code/0x10) % 0x10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[code % 0x10]; + leds |= 0x6d010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_minssecs(unsigned int time, unsigned int points) +{ + u32 leds; + + leds = asb2305_led_hex_tbl[(time/600) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/60) % 10]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[(time/10) % 6]; + leds <<= 8; + leds |= asb2305_led_hex_tbl[time % 10]; + leds |= points^0x01010101; + + ASB2305_7SEGLEDS = leds; +} + +void peripheral_leds7x4_display_rtc(void) +{ + unsigned int clock; + u8 mins, secs; + + mins = RTMCR; + secs = RTSCR; + + clock = ((mins & 0xf0) >> 4); + clock *= 10; + clock += (mins & 0x0f); + clock *= 6; + + clock += ((secs & 0xf0) >> 4); + clock *= 10; + clock += (secs & 0x0f); + + peripheral_leds7x4_display_minssecs(clock, 0); +} + +void peripheral_leds_led_chase(void) +{ + ASB2305_7SEGLEDS = asb2305_led_chase_tbl[asb2305_led_chase]; + asb2305_led_chase++; + if (asb2305_led_chase >= 6) + asb2305_led_chase = 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c new file mode 100644 index 00000000000..d100ca78846 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -0,0 +1,303 @@ +/* ASB2305 PCI resource stuff + * + * Copyright (C) 2001 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/pci-i386.c + * - Copyright 1997--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include "pci-asb2305.h" + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void pcibios_align_resource(void *data, struct resource *res, + resource_size_t size, resource_size_t align) +{ +#if 0 + struct pci_dev *dev = data; + + printk(KERN_DEBUG + "### PCIBIOS_ALIGN_RESOURCE(%s,,{%08lx-%08lx,%08lx},%lx)\n", + pci_name(dev), + res->start, + res->end, + res->flags, + size + ); +#endif + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ +static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + list_for_each_entry(bus, bus_list, node) { + dev = bus->self; + if (dev) { + for (idx = PCI_BRIDGE_RESOURCES; + idx < PCI_NUM_RESOURCES; + idx++) { + r = &dev->resource[idx]; + if (!r->flags) + continue; + pr = pci_find_parent_resource(dev, r); + if (!r->start || + !pr || + request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of bridge %s\n", + idx, pci_name(dev)); + /* Something is wrong with the region. + * Invalidate the resource to prevent + * child resource allocations in this + * range. */ + r->flags = 0; + } + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev = NULL; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + for_each_pci_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for (idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned */ + continue; + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + DBG("PCI[%s]: Resource %08lx-%08lx" + " (f=%lx, d=%d, p=%d)\n", + pci_name(dev), r->start, r->end, r->flags, + disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + printk(KERN_ERR "PCI:" + " Cannot allocate resource" + " region %d of device %s\n", + idx, pci_name(dev)); + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & IORESOURCE_ROM_ENABLE) { + /* Turn the ROM off, leave the resource region, + * but keep it unregistered. */ + u32 reg; + DBG("PCI: Switching off ROM of %s\n", + pci_name(dev)); + r->flags &= ~IORESOURCE_ROM_ENABLE; + pci_read_config_dword( + dev, dev->rom_base_reg, ®); + pci_write_config_dword( + dev, dev->rom_base_reg, + reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static int __init pcibios_assign_resources(void) +{ + struct pci_dev *dev = NULL; + struct resource *r, *pr; + + if (!(pci_probe & PCI_ASSIGN_ROMS)) { + /* Try to use BIOS settings for ROMs, otherwise let + pci_assign_unassigned_resources() allocate the new + addresses. */ + for_each_pci_dev(dev) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (!r->flags || !r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + r->end -= r->start; + r->start = 0; + } + } + } + + pci_assign_unassigned_resources(); + + return 0; +} + +fs_initcall(pcibios_assign_resources); + +void __init pcibios_resource_survey(void) +{ + DBG("PCI: Allocating resources\n"); + pcibios_allocate_bus_resources(&pci_root_buses); + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); +} + +int pcibios_enable_resources(struct pci_dev *dev, int mask) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + + for (idx = 0; idx < 6; idx++) { + /* Only set up the requested stuff */ + if (!(mask & (1 << idx))) + continue; + + r = &dev->resource[idx]; + + if (!r->start && r->end) { + printk(KERN_ERR + "PCI: Device %s not available because of" + " resource collisions\n", + pci_name(dev)); + return -EINVAL; + } + + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + + if (cmd != old_cmd) + pci_write_config_word(dev, PCI_COMMAND, cmd); + + return 0; +} + +/* + * If we set up a device for bus mastering, we need to check the latency + * timer as certain crappy BIOSes forget to set it properly. + */ +unsigned int pcibios_max_latency = 255; + +void pcibios_set_master(struct pci_dev *dev) +{ + u8 lat; + + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); + + if (lat < 16) + lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency; + else if (lat > pcibios_max_latency) + lat = pcibios_max_latency; + else + return; + + pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + unsigned long prot; + + /* Leave vm_pgoff as-is, the PCI space address is the physical + * address on this platform. + */ + vma->vm_flags |= VM_LOCKED | VM_IO; + + prot = pgprot_val(vma->vm_page_prot); + prot &= ~_PAGE_CACHE; + vma->vm_page_prot = __pgprot(prot); + + /* Write-combine setting is ignored */ + if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.h b/arch/mn10300/unit-asb2305/pci-asb2305.h new file mode 100644 index 00000000000..84634fa3bce --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-asb2305.h @@ -0,0 +1,82 @@ +/* ASB2305 Arch-specific PCI declarations + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from: arch/i386/kernel/pci-i386.h: (c) 1999 Martin Mares <mj@ucw.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#ifndef _PCI_ASB2305_H +#define _PCI_ASB2305_H + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#define PCI_PROBE_BIOS 1 +#define PCI_PROBE_CONF1 2 +#define PCI_PROBE_CONF2 4 +#define PCI_NO_SORT 0x100 +#define PCI_BIOS_SORT 0x200 +#define PCI_NO_CHECKS 0x400 +#define PCI_ASSIGN_ROMS 0x1000 +#define PCI_BIOS_IRQ_SCAN 0x2000 + +extern unsigned int pci_probe; + +/* pci-asb2305.c */ + +extern unsigned int pcibios_max_latency; + +extern void pcibios_resource_survey(void); +extern int pcibios_enable_resources(struct pci_dev *dev, int mask); + +/* pci.c */ + +extern int pcibios_last_bus; +extern struct pci_bus *pci_root_bus; +extern struct pci_ops *pci_root_ops; + +extern struct irq_routing_table *pcibios_get_irq_routing_table(void); +extern int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); + +/* pci-irq.c */ + +struct irq_info { + u8 bus, devfn; /* Bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, + * 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__((packed)) irq[4]; + u8 slot; /* Slot number, 0=onboard */ + u8 rfu; +} __attribute__((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__((packed)); + +extern unsigned int pcibios_irq_mask; + +extern void pcibios_irq_init(void); +extern void pcibios_fixup_irqs(void); +extern void pcibios_enable_irq(struct pci_dev *dev); + +#endif /* PCI_ASB2305_H */ diff --git a/arch/mn10300/unit-asb2305/pci-iomap.c b/arch/mn10300/unit-asb2305/pci-iomap.c new file mode 100644 index 00000000000..dbceae4307d --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-iomap.c @@ -0,0 +1,31 @@ +/* ASB2305 PCI I/O mapping handler + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/pci.h> +#include <linux/module.h> + +/* + * Create a virtual mapping cookie for a PCI BAR (memory or IO) + */ +void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) +{ + 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 || !start) + return NULL; + + if ((flags & IORESOURCE_IO) || (flags & IORESOURCE_MEM)) + return (void __iomem *) start; + + return NULL; +} +EXPORT_SYMBOL(pci_iomap); diff --git a/arch/mn10300/unit-asb2305/pci-irq.c b/arch/mn10300/unit-asb2305/pci-irq.c new file mode 100644 index 00000000000..58cfb44f0ac --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci-irq.c @@ -0,0 +1,51 @@ +/* PCI IRQ routing on the MN103E010 based ASB2305 + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + * + * This is simple: All PCI interrupts route through the CPU's XIRQ1 pin [IRQ 35] + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <asm/io.h> +#include <asm/smp.h> +#include "pci-asb2305.h" + +void __init pcibios_irq_init(void) +{ +} + +void __init pcibios_fixup_irqs(void) +{ + struct pci_dev *dev = NULL; + u8 line, pin; + + while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (pin) { + dev->irq = XIRQ1; + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, + dev->irq); + } + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line); + } +} + +void __init pcibios_penalize_isa_irq(int irq) +{ +} + +void pcibios_enable_irq(struct pci_dev *dev) +{ + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); +} diff --git a/arch/mn10300/unit-asb2305/pci.c b/arch/mn10300/unit-asb2305/pci.c new file mode 100644 index 00000000000..1a86425fec4 --- /dev/null +++ b/arch/mn10300/unit-asb2305/pci.c @@ -0,0 +1,545 @@ +/* ASB2305 PCI support + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * Derived from arch/i386/kernel/pci-pc.c + * (c) 1999--2000 Martin Mares <mj@suse.cz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <asm/io.h> +#include "pci-asb2305.h" + +unsigned int pci_probe = 1; + +int pcibios_last_bus = -1; +struct pci_bus *pci_root_bus; +struct pci_ops *pci_root_ops; + +/* + * Functions for accessing PCI configuration space + */ + +#define CONFIG_CMD(bus, devfn, where) \ + (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) + +#define MEM_PAGING_REG (*(volatile __u32 *) 0xBFFFFFF4) +#define CONFIG_ADDRESS (*(volatile __u32 *) 0xBFFFFFF8) +#define CONFIG_DATAL(X) (*(volatile __u32 *) 0xBFFFFFFC) +#define CONFIG_DATAW(X) (*(volatile __u16 *) (0xBFFFFFFC + ((X) & 2))) +#define CONFIG_DATAB(X) (*(volatile __u8 *) (0xBFFFFFFC + ((X) & 3))) + +#define BRIDGEREGB(X) (*(volatile __u8 *) (0xBE040000 + (X))) +#define BRIDGEREGW(X) (*(volatile __u16 *) (0xBE040000 + (X))) +#define BRIDGEREGL(X) (*(volatile __u32 *) (0xBE040000 + (X))) + +static inline int __query(const struct pci_bus *bus, unsigned int devfn) +{ +#if 0 + return bus->number == 0 && (devfn == PCI_DEVFN(0, 0)); + return bus->number == 1; + return bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) || devfn == PCI_DEVFN(3, 0)); +#endif + return 1; +} + +/* + * translate Linuxcentric addresses to PCI bus addresses + */ +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + if (res->flags & IORESOURCE_IO) { + region->start = (res->start & 0x00ffffff); + region->end = (res->end & 0x00ffffff); + } + + if (res->flags & IORESOURCE_MEM) { + region->start = (res->start & 0x03ffffff) | MEM_PAGING_REG; + region->end = (res->end & 0x03ffffff) | MEM_PAGING_REG; + } + +#if 0 + printk(KERN_DEBUG "RES->BUS: %lx-%lx => %lx-%lx\n", + res->start, res->end, region->start, region->end); +#endif +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +/* + * translate PCI bus addresses to Linuxcentric addresses + */ +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + if (res->flags & IORESOURCE_IO) { + res->start = (region->start & 0x00ffffff) | 0xbe000000; + res->end = (region->end & 0x00ffffff) | 0xbe000000; + } + + if (res->flags & IORESOURCE_MEM) { + res->start = (region->start & 0x03ffffff) | 0xb8000000; + res->end = (region->end & 0x03ffffff) | 0xb8000000; + } + +#if 0 + printk(KERN_INFO "BUS->RES: %lx-%lx => %lx-%lx\n", + region->start, region->end, res->start, res->end); +#endif +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + +/* + * + */ +static int pci_ampci_read_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGB(where); + __pcbdebug("=> %02hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAB(where); + if (__query(bus, devfn)) + __pcidebug("=> %02hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGW(where); + __pcbdebug("=> %04hx", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAW(where); + if (__query(bus, devfn)) + __pcidebug("=> %04hx", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 *_value) +{ + u32 rawval, value; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + value = BRIDGEREGL(where); + __pcbdebug("=> %08x", &BRIDGEREGL(where), value); + } else { + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + value = CONFIG_DATAL(where); + if (__query(bus, devfn)) + __pcidebug("=> %08x", bus, devfn, where, value); + } + + *_value = value; + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_byte(struct pci_bus *bus, unsigned int devfn, + int where, u8 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %02x", &BRIDGEREGB(where), value); + BRIDGEREGB(where) = value; + } else { + if (bus->number == 0 && + (devfn == PCI_DEVFN(2, 0) && devfn == PCI_DEVFN(3, 0)) + ) + __pcidebug("<= %02x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAB(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_word(struct pci_bus *bus, unsigned int devfn, + int where, u16 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %04hx", &BRIDGEREGW(where), value); + BRIDGEREGW(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %04hx", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAW(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_write_config_dword(struct pci_bus *bus, unsigned int devfn, + int where, u32 value) +{ + u32 rawval; + + if (bus->number == 0 && devfn == PCI_DEVFN(0, 0)) { + __pcbdebug("<= %08x", &BRIDGEREGL(where), value); + BRIDGEREGL(where) = value; + } else { + if (__query(bus, devfn)) + __pcidebug("<= %08x", bus, devfn, where, value); + CONFIG_ADDRESS = CONFIG_CMD(bus, devfn, where); + rawval = CONFIG_ADDRESS; + CONFIG_DATAL(where) = value; + } + return PCIBIOS_SUCCESSFUL; +} + +static int pci_ampci_read_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + switch (size) { + case 1: + return pci_ampci_read_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_read_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_read_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static int pci_ampci_write_config(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + switch (size) { + case 1: + return pci_ampci_write_config_byte(bus, devfn, where, val); + case 2: + return pci_ampci_write_config_word(bus, devfn, where, val); + case 4: + return pci_ampci_write_config_dword(bus, devfn, where, val); + default: + BUG(); + return -EOPNOTSUPP; + } +} + +static struct pci_ops pci_direct_ampci = { + pci_ampci_read_config, + pci_ampci_write_config, +}; + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int __init pci_sanity_check(struct pci_ops *o) +{ + struct pci_bus bus; /* Fake bus and device */ + u32 x; + + bus.number = 0; + + if ((!o->read(&bus, 0, PCI_CLASS_DEVICE, 2, &x) && + (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) || + (!o->read(&bus, 0, PCI_VENDOR_ID, 2, &x) && + (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ))) + return 1; + + printk(KERN_ERROR "PCI: Sanity check failed\n"); + return 0; +} + +static int __init pci_check_direct(void) +{ + unsigned long flags; + + local_irq_save(flags); + + /* + * Check if access works. + */ + if (pci_sanity_check(&pci_direct_ampci)) { + local_irq_restore(flags); + printk(KERN_INFO "PCI: Using configuration ampci\n"); + request_mem_region(0xBE040000, 256, "AMPCI bridge"); + request_mem_region(0xBFFFFFF4, 12, "PCI ampci"); + return 0; + } + + local_irq_restore(flags); + return -ENODEV; +} + +static int __devinit is_valid_resource(struct pci_dev *dev, int idx) +{ + unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; + struct resource *devr = &dev->resource[idx]; + + if (dev->bus) { + for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { + struct resource *busr = dev->bus->resource[i]; + + if (!busr || (busr->flags ^ devr->flags) & type_mask) + continue; + + if (devr->start && + devr->start >= busr->start && + devr->end <= busr->end) + return 1; + } + } + + return 0; +} + +static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev) +{ + struct pci_bus_region region; + int i; + int limit; + + if (dev->bus->number != 0) + return; + + limit = (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) ? + PCI_BRIDGE_RESOURCES : PCI_NUM_RESOURCES; + + for (i = 0; i < limit; i++) { + if (!dev->resource[i].flags) + continue; + + region.start = dev->resource[i].start; + region.end = dev->resource[i].end; + pcibios_bus_to_resource(dev, &dev->resource[i], ®ion); + if (is_valid_resource(dev, i)) + pci_claim_resource(dev, i); + } +} + +/* + * Called after each bus is probed, but before its children + * are examined. + */ +void __devinit pcibios_fixup_bus(struct pci_bus *bus) +{ + struct pci_dev *dev; + + if (bus->self) { + pci_read_bridge_bases(bus); + pcibios_fixup_device_resources(bus->self); + } + + list_for_each_entry(dev, &bus->devices, bus_list) + pcibios_fixup_device_resources(dev); +} + +/* + * Initialization. Try all known PCI access methods. Note that we support + * using both PCI BIOS and direct access: in such cases, we use I/O ports + * to access config space, but we still keep BIOS order of cards to be + * compatible with 2.0.X. This should go away some day. + */ +static int __init pcibios_init(void) +{ + ioport_resource.start = 0xA0000000; + ioport_resource.end = 0xDFFFFFFF; + iomem_resource.start = 0xA0000000; + iomem_resource.end = 0xDFFFFFFF; + + if (!pci_probe) + return 0; + + if (pci_check_direct() < 0) { + printk(KERN_WARNING "PCI: No PCI bus detected\n"); + return 0; + } + + printk(KERN_INFO "PCI: Probing PCI hardware [mempage %08x]\n", + MEM_PAGING_REG); + + { +#if 0 + static struct pci_bus am33_root_bus = { + .children = LIST_HEAD_INIT(am33_root_bus.children), + .devices = LIST_HEAD_INIT(am33_root_bus.devices), + .number = 0, + .secondary = 0, + .resource = { &ioport_resource, &iomem_resource }, + }; + + am33_root_bus.ops = pci_root_ops; + list_add_tail(&am33_root_bus.node, &pci_root_buses); + + am33_root_bus.subordinate = pci_do_scan_bus(0); + + pci_root_bus = &am33_root_bus; +#else + pci_root_bus = pci_scan_bus(0, &pci_direct_ampci, NULL); +#endif + } + + pcibios_irq_init(); + pcibios_fixup_irqs(); +#if 0 + pcibios_resource_survey(); +#endif + return 0; +} + +arch_initcall(pcibios_init); + +char *__init pcibios_setup(char *str) +{ + if (!strcmp(str, "off")) { + pci_probe = 0; + return NULL; + + } else if (!strncmp(str, "lastbus=", 8)) { + pcibios_last_bus = simple_strtol(str+8, NULL, 0); + return NULL; + } + + return str; +} + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int err; + + err = pcibios_enable_resources(dev, mask); + if (err == 0) + pcibios_enable_irq(dev); + return err; +} + +/* + * disable the ethernet chipset + */ +static void __init unit_disable_pcnet(struct pci_bus *bus, struct pci_ops *o) +{ + u32 x; + + bus->number = 0; + + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, x); + o->read (bus, PCI_DEVFN(2, 0), PCI_COMMAND, 2, &x); + o->write(bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, 0x00030001); + o->read (bus, PCI_DEVFN(2, 0), PCI_BASE_ADDRESS_0, 4, &x); + +#define RDP (*(volatile u32 *) 0xBE030010) +#define RAP (*(volatile u32 *) 0xBE030014) +#define __set_RAP(X) do { RAP = (X); x = RAP; } while (0) +#define __set_RDP(X) do { RDP = (X); x = RDP; } while (0) +#define __get_RDP() ({ RDP & 0xffff; }) + + __set_RAP(0); + __set_RDP(0x0004); /* CSR0 = STOP */ + + __set_RAP(88); /* check CSR88 indicates an Am79C973 */ + BUG_ON(__get_RDP() != 0x5003); + + for (x = 0; x < 100; x++) + asm volatile("nop"); + + __set_RDP(0x0004); /* CSR0 = STOP */ +} + +/* + * initialise the unit hardware + */ +asmlinkage void __init unit_pci_init(void) +{ + struct pci_bus bus; /* Fake bus and device */ + struct pci_ops *o = &pci_direct_ampci; + u32 x; + + set_intr_level(XIRQ1, GxICR_LEVEL_3); + + memset(&bus, 0, sizeof(bus)); + + MEM_PAGING_REG = 0xE8000000; + + /* we need to set up the bridge _now_ or we won't be able to access the + * PCI config registers + */ + BRIDGEREGW(PCI_COMMAND) |= + PCI_COMMAND_SERR | PCI_COMMAND_PARITY | + PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_MASTER; + BRIDGEREGW(PCI_STATUS) = 0xF800; + BRIDGEREGB(PCI_LATENCY_TIMER) = 0x10; + BRIDGEREGL(PCI_BASE_ADDRESS_0) = 0x80000000; + BRIDGEREGB(PCI_INTERRUPT_LINE) = 1; + BRIDGEREGL(0x48) = 0x98000000; /* AMPCI base addr */ + BRIDGEREGB(0x41) = 0x00; /* secondary bus + * number */ + BRIDGEREGB(0x42) = 0x01; /* subordinate bus + * number */ + BRIDGEREGB(0x44) = 0x01; + BRIDGEREGL(0x50) = 0x00000001; + BRIDGEREGL(0x58) = 0x00001002; + BRIDGEREGL(0x5C) = 0x00000011; + + /* we also need to set up the PCI-PCI bridge */ + bus.number = 0; + + /* IO: 0x00000000-0x00020000 */ + o->read (&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, &x); + x |= PCI_COMMAND_MASTER | + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_SERR | PCI_COMMAND_PARITY; + o->write(&bus, PCI_DEVFN(3, 0), PCI_COMMAND, 2, x); + + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, 0x01); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE, 1, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, 0x00020000); + o->read (&bus, PCI_DEVFN(3, 0), PCI_IO_BASE_UPPER16, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, 0xEBB0EA00); + o->read (&bus, PCI_DEVFN(3, 0), PCI_MEMORY_BASE, 4, &x); + o->write(&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, 0xE9F0E800); + o->read (&bus, PCI_DEVFN(3, 0), PCI_PREF_MEMORY_BASE, 4, &x); + + unit_disable_pcnet(&bus, o); +} diff --git a/arch/mn10300/unit-asb2305/unit-init.c b/arch/mn10300/unit-asb2305/unit-init.c new file mode 100644 index 00000000000..6a352414a35 --- /dev/null +++ b/arch/mn10300/unit-asb2305/unit-init.c @@ -0,0 +1,61 @@ +/* ASB2305 Initialisation + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/io.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/cpu/intctl-regs.h> +#include <asm/cpu/rtc-regs.h> +#include <asm/cpu/serial-regs.h> +#include <asm/unit/serial.h> + +/* + * initialise some of the unit hardware before gdbstub is set up + */ +asmlinkage void __init unit_init(void) +{ +#ifndef CONFIG_GDBSTUB_ON_TTYSx + /* set the 16550 interrupt line to level 3 if not being used for GDB */ + set_intr_level(XIRQ0, GxICR_LEVEL_3); +#endif +} + +/* + * initialise the rest of the unit hardware after gdbstub is ready + */ +void __init unit_setup(void) +{ +#ifdef CONFIG_PCI + unit_pci_init(); +#endif +} + +/* + * initialise the external interrupts used by a unit of this type + */ +void __init unit_init_IRQ(void) +{ + unsigned int extnum; + + for (extnum = 0; extnum < NR_XIRQS; extnum++) { + switch (GET_XIRQ_TRIGGER(extnum)) { + case XIRQ_TRIGGER_HILEVEL: + case XIRQ_TRIGGER_LOWLEVEL: + set_irq_handler(XIRQ2IRQ(extnum), handle_level_irq); + break; + default: + break; + } + } +} diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 028d8a0fdbf..d929ac84f25 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -75,6 +75,9 @@ config IRQ_PER_CPU bool default y +config ARCH_SUPPORTS_AOUT + def_bool y + # unless you want to implement ACPI on PA-RISC ... ;-) config PM bool diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c index c44b8c51f5d..39e7c5a5946 100644 --- a/arch/parisc/kernel/setup.c +++ b/arch/parisc/kernel/setup.c @@ -190,7 +190,7 @@ c_stop (struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index aa875fa4348..eb80f5e33d7 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -315,11 +315,13 @@ static void __init setup_bootmem(void) #define PDC_CONSOLE_IO_IODC_SIZE 32768 reserve_bootmem_node(NODE_DATA(0), 0UL, - (unsigned long)(PAGE0->mem_free + PDC_CONSOLE_IO_IODC_SIZE)); + (unsigned long)(PAGE0->mem_free + + PDC_CONSOLE_IO_IODC_SIZE), BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(0), __pa((unsigned long)_text), - (unsigned long)(_end - _text)); + (unsigned long)(_end - _text), BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(0), (bootmap_start_pfn << PAGE_SHIFT), - ((bootmap_pfn - bootmap_start_pfn) << PAGE_SHIFT)); + ((bootmap_pfn - bootmap_start_pfn) << PAGE_SHIFT), + BOOTMEM_DEFAULT); #ifndef CONFIG_DISCONTIGMEM @@ -328,7 +330,8 @@ static void __init setup_bootmem(void) for (i = 0; i < npmem_holes; i++) { reserve_bootmem_node(NODE_DATA(0), (pmem_holes[i].start_pfn << PAGE_SHIFT), - (pmem_holes[i].pages << PAGE_SHIFT)); + (pmem_holes[i].pages << PAGE_SHIFT), + BOOTMEM_DEFAULT); } #endif @@ -346,7 +349,8 @@ static void __init setup_bootmem(void) initrd_below_start_ok = 1; printk(KERN_INFO "initrd: reserving %08lx-%08lx (mem_max %08lx)\n", __pa(initrd_start), __pa(initrd_start) + initrd_reserve, mem_max); - reserve_bootmem_node(NODE_DATA(0),__pa(initrd_start), initrd_reserve); + reserve_bootmem_node(NODE_DATA(0), __pa(initrd_start), + initrd_reserve, BOOTMEM_DEFAULT); } } #endif diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index cf030b00441..26b963c33c8 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -97,6 +97,7 @@ config EARLY_PRINTK config COMPAT bool default y if PPC64 + select COMPAT_BINFMT_ELF config SYSVIPC_COMPAT bool @@ -271,6 +272,12 @@ config HOTPLUG_CPU config ARCH_ENABLE_MEMORY_HOTPLUG def_bool y +config ARCH_HAS_WALK_MEMORY + def_bool y + +config ARCH_ENABLE_MEMORY_HOTREMOVE + def_bool y + config KEXEC bool "kexec system call (EXPERIMENTAL)" depends on (PPC_PRPMC2800 || PPC_MULTIPLATFORM) && EXPERIMENTAL @@ -438,25 +445,6 @@ config WANT_DEVICE_TREE bool default n -config DEVICE_TREE - string "Static device tree source file" - depends on WANT_DEVICE_TREE - help - This specifies the device tree source (.dts) file to be - compiled and included when building the bootwrapper. If a - relative filename is given, then it will be relative to - arch/powerpc/boot/dts. If you are not using the bootwrapper, - or do not need to build a dts into the bootwrapper, this - field is ignored. - - For example, this is required when building a cuImage target - for an older U-Boot, which cannot pass a device tree itself. - Such a kernel will not work with a newer U-Boot that tries to - pass a device tree (unless you tell it not to). If your U-Boot - does not mention a device tree in "help bootm", then use the - cuImage target and specify a device tree here. Otherwise, use - the uImage target and leave this field blank. - endmenu config ISA_DMA_API @@ -512,7 +500,7 @@ config PCI bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \ || PPC_MPC52xx || (EMBEDDED && (PPC_PSERIES || PPC_ISERIES)) \ || PPC_PS3 || 44x - default y if !40x && !CPM2 && !8xx && !PPC_83xx \ + default y if !40x && !CPM2 && !8xx && !PPC_MPC512x && !PPC_83xx \ && !PPC_85xx && !PPC_86xx default PCI_PERMEDIA if !4xx && !CPM2 && !8xx default PCI_QSPAN if !4xx && !CPM2 && 8xx diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index f70df9b64f8..6845482f009 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -151,14 +151,11 @@ core-$(CONFIG_XMON) += arch/powerpc/xmon/ drivers-$(CONFIG_OPROFILE) += arch/powerpc/oprofile/ # Default to zImage, override when needed -defaultimage-y := zImage -defaultimage-$(CONFIG_DEFAULT_UIMAGE) := uImage -KBUILD_IMAGE := $(defaultimage-y) -all: $(KBUILD_IMAGE) +all: zImage CPPFLAGS_vmlinux.lds := -Upowerpc -BOOT_TARGETS = zImage zImage.initrd uImage +BOOT_TARGETS = zImage zImage.initrd uImage treeImage.% cuImage.% PHONY += $(BOOT_TARGETS) @@ -180,7 +177,7 @@ define archhelp endef install: vdso_install - $(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install + $(Q)$(MAKE) $(build)=$(boot) install vdso_install: ifeq ($(CONFIG_PPC64),y) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 122a2707899..49797a45416 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -60,8 +60,9 @@ src-wlib := string.S crt0.S stdio.c main.c \ src-plat := of.c cuboot-52xx.c cuboot-824x.c cuboot-83xx.c cuboot-85xx.c holly.c \ cuboot-ebony.c treeboot-ebony.c prpmc2800.c \ ps3-head.S ps3-hvcall.S ps3.c treeboot-bamboo.c cuboot-8xx.c \ - cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c cuboot-bamboo.c \ - fixed-head.S ep88xc.c cuboot-hpc2.c ep405.c cuboot-taishan.c \ + cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c \ + cuboot-bamboo.c cuboot-mpc7448hpc2.c cuboot-taishan.c \ + fixed-head.S ep88xc.c ep405.c \ cuboot-katmai.c cuboot-rainier.c redboot-8xx.c ep8248e.c \ cuboot-warp.c cuboot-85xx-cpm2.c src-boot := $(src-wlib) $(src-plat) empty.c @@ -123,6 +124,8 @@ targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a) extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ $(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds +dtstree := $(srctree)/$(src)/dts + wrapper :=$(srctree)/$(src)/wrapper wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree dtc) \ $(wrapper) FORCE @@ -181,7 +184,7 @@ quiet_cmd_wrap = WRAP $@ image-$(CONFIG_PPC_PSERIES) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.pseries image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries -image-$(CONFIG_PPC_PS3) += zImage.ps3 +image-$(CONFIG_PPC_PS3) += zImage-dtb.ps3 image-$(CONFIG_PPC_CELLEB) += zImage.pseries image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp @@ -191,33 +194,69 @@ image-$(CONFIG_PPC_PRPMC2800) += zImage.prpmc2800 image-$(CONFIG_PPC_ISERIES) += zImage.iseries image-$(CONFIG_DEFAULT_UIMAGE) += uImage -ifneq ($(CONFIG_DEVICE_TREE),"") -image-$(CONFIG_PPC_8xx) += cuImage.8xx -image-$(CONFIG_PPC_EP88XC) += zImage.ep88xc -image-$(CONFIG_EP405) += zImage.ep405 -image-$(CONFIG_8260) += cuImage.pq2 -image-$(CONFIG_EP8248E) += zImage.ep8248e -image-$(CONFIG_PPC_MPC52xx) += cuImage.52xx -image-$(CONFIG_STORCENTER) += cuImage.824x -image-$(CONFIG_PPC_83xx) += cuImage.83xx -image-$(CONFIG_PPC_85xx) += cuImage.85xx -ifeq ($(CONFIG_CPM2),y) -image-$(CONFIG_PPC_85xx) += cuImage.85xx-cpm2 -endif -image-$(CONFIG_MPC7448HPC2) += cuImage.hpc2 +# +# Targets which embed a device tree blob +# +# Theses are default targets to build images which embed device tree blobs. +# They are only required on boards which do not have FDT support in firmware. +# Boards with newish u-boot firmare can use the uImage target above +# + +# Board ports in arch/powerpc/platform/40x/Kconfig +image-$(CONFIG_EP405) += zImage-dtb.ep405 +image-$(CONFIG_WALNUT) += treeImage.walnut + +# Board ports in arch/powerpc/platform/44x/Kconfig image-$(CONFIG_EBONY) += treeImage.ebony cuImage.ebony image-$(CONFIG_BAMBOO) += treeImage.bamboo cuImage.bamboo image-$(CONFIG_SEQUOIA) += cuImage.sequoia image-$(CONFIG_RAINIER) += cuImage.rainier -image-$(CONFIG_WALNUT) += treeImage.walnut image-$(CONFIG_TAISHAN) += cuImage.taishan image-$(CONFIG_KATMAI) += cuImage.katmai image-$(CONFIG_WARP) += cuImage.warp -endif -ifneq ($(CONFIG_REDBOOT),"") -image-$(CONFIG_PPC_8xx) += zImage.redboot-8xx -endif +# Board ports in arch/powerpc/platform/8xx/Kconfig +image-$(CONFIG_PPC_MPC86XADS) += cuImage.mpc866ads +image-$(CONFIG_PPC_MPC885ADS) += cuImage.mpc885ads +image-$(CONFIG_PPC_EP88XC) += zImage-dtb.ep88xc +image-$(CONFIG_PPC_ADDER875) += cuImage.adder875-uboot \ + zImage-dtb.adder875-redboot + +# Board ports in arch/powerpc/platform/52xx/Kconfig +image-$(CONFIG_PPC_LITE5200) += cuImage.lite5200 cuImage.lite5200b + +# Board ports in arch/powerpc/platform/82xx/Kconfig +image-$(CONFIG_MPC8272_ADS) += cuImage.mpc8272ads +image-$(CONFIG_PQ2FADS) += cuImage.pq2fads +image-$(CONFIG_EP8248E) += zImage-dtb.ep8248e + +# Board ports in arch/powerpc/platform/83xx/Kconfig +image-$(CONFIG_MPC832x_MDS) += cuImage.mpc832x_mds +image-$(CONFIG_MPC832x_RDB) += cuImage.mpc832x_rdb +image-$(CONFIG_MPC834x_ITX) += cuImage.mpc8349emitx \ + cuImage.mpc8349emitxgp +image-$(CONFIG_MPC834x_MDS) += cuImage.mpc834x_mds +image-$(CONFIG_MPC836x_MDS) += cuImage.mpc836x_mds + +# Board ports in arch/powerpc/platform/85xx/Kconfig +image-$(CONFIG_MPC8540_ADS) += cuImage.mpc8540ads +image-$(CONFIG_MPC8560_ADS) += cuImage.mpc8560ads +image-$(CONFIG_MPC85xx_CDS) += cuImage.mpc8541cds \ + cuImage.mpc8548cds \ + cuImage.mpc8555cds +image-$(CONFIG_MPC85xx_MDS) += cuImage.mpc8568mds +image-$(CONFIG_MPC85xx_DS) += cuImage.mpc8544ds \ + cuImage.mpc8572ds +image-$(CONFIG_TQM8540) += cuImage.tqm8540 +image-$(CONFIG_TQM8541) += cuImage.tqm8541 +image-$(CONFIG_TQM8555) += cuImage.tqm8555 +image-$(CONFIG_TQM8560) += cuImage.tqm8560 +image-$(CONFIG_SBC8548) += cuImage.tqm8548 +image-$(CONFIG_SBC8560) += cuImage.tqm8560 + +# Board ports in arch/powerpc/platform/embedded6xx/Kconfig +image-$(CONFIG_STORCENTER) += cuImage.storcenter +image-$(CONFIG_MPC7448HPC2) += cuImage.mpc7448hpc2 # For 32-bit powermacs, build the COFF and miboot images # as well as the ELF images. @@ -233,24 +272,20 @@ targets += $(image-y) $(initrd-y) $(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz -# If CONFIG_WANT_DEVICE_TREE is set and CONFIG_DEVICE_TREE isn't an -# empty string, define 'dts' to be path to the dts -# CONFIG_DEVICE_TREE will have "" around it, make sure to strip them -ifeq ($(CONFIG_WANT_DEVICE_TREE),y) -ifneq ($(CONFIG_DEVICE_TREE),"") -dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\ - ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE:"%"=%) -endif -endif - # Don't put the ramdisk on the pattern rule; when its missing make will try # the pattern rule with less dependencies that also matches (even with the # hard dependency listed). -$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) $(dts) - $(call if_changed,wrap,$*,$(dts),,$(obj)/ramdisk.image.gz) +$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz) -$(obj)/zImage.%: vmlinux $(wrapperbits) $(dts) - $(call if_changed,wrap,$*,$(dts)) +$(obj)/zImage.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*) + +$(obj)/zImage-dtb.initrd.%: vmlinux $(wrapperbits) $(dtstree)/%.dts + $(call if_changed,wrap,$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz) + +$(obj)/zImage-dtb.%: vmlinux $(wrapperbits) $(dtstree)/%.dts + $(call if_changed,wrap,$*,$(dtstree)/$*.dts) # This cannot be in the root of $(src) as the zImage rule always adds a $(obj) # prefix @@ -260,24 +295,17 @@ $(obj)/vmlinux.strip: vmlinux $(obj)/zImage.iseries: vmlinux $(STRIP) -s -R .comment $< -o $@ -$(obj)/zImage.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts - $(STRIP) -s -R .comment $< -o vmlinux.strip - $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,) - -$(obj)/zImage.initrd.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts $(obj)/ramdisk.image.gz - $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,$(obj)/ramdisk.image.gz) - $(obj)/uImage: vmlinux $(wrapperbits) $(call if_changed,wrap,uboot) -$(obj)/cuImage.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,cuboot-$*,$(dts)) +$(obj)/cuImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,cuboot-$*,$(dtstree)/$*.dts) -$(obj)/treeImage.initrd.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,treeboot-$*,$(dts),,$(obj)/ramdisk.image.gz) +$(obj)/treeImage.initrd.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz) -$(obj)/treeImage.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,treeboot-$*,$(dts)) +$(obj)/treeImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts) # If there isn't a platform selected then just strip the vmlinux. ifeq (,$(image-y)) diff --git a/arch/powerpc/boot/cuboot-hpc2.c b/arch/powerpc/boot/cuboot-mpc7448hpc2.c index 1b8953259d7..1b8953259d7 100644 --- a/arch/powerpc/boot/cuboot-hpc2.c +++ b/arch/powerpc/boot/cuboot-mpc7448hpc2.c diff --git a/arch/powerpc/boot/dts/adder875-redboot.dts b/arch/powerpc/boot/dts/adder875-redboot.dts index 930bfb3894e..28e9cd3d7a2 100644 --- a/arch/powerpc/boot/dts/adder875-redboot.dts +++ b/arch/powerpc/boot/dts/adder875-redboot.dts @@ -151,6 +151,7 @@ compatible = "fsl,mpc875-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <50000000>; reg = <0x9f0 0x10>; }; diff --git a/arch/powerpc/boot/dts/adder875-uboot.dts b/arch/powerpc/boot/dts/adder875-uboot.dts index 0197242dacf..54fb60ec03e 100644 --- a/arch/powerpc/boot/dts/adder875-uboot.dts +++ b/arch/powerpc/boot/dts/adder875-uboot.dts @@ -150,6 +150,7 @@ compatible = "fsl,mpc875-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <50000000>; reg = <0x9f0 0x10>; }; diff --git a/arch/powerpc/boot/dts/mpc5121ads.dts b/arch/powerpc/boot/dts/mpc5121ads.dts new file mode 100644 index 00000000000..94ad7b2b241 --- /dev/null +++ b/arch/powerpc/boot/dts/mpc5121ads.dts @@ -0,0 +1,122 @@ +/* + * MPC5121E MDS Device Tree Source + * + * Copyright 2007 Freescale Semiconductor Inc. + * + * 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. + */ + +/dts-v1/; + +/ { + model = "mpc5121ads"; + compatible = "fsl,mpc5121ads"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,5121@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <0x20>; // 32 bytes + i-cache-line-size = <0x20>; // 32 bytes + d-cache-size = <0x8000>; // L1, 32K + i-cache-size = <0x8000>; // L1, 32K + timebase-frequency = <49500000>;// 49.5 MHz (csb/4) + bus-frequency = <198000000>; // 198 MHz csb bus + clock-frequency = <396000000>; // 396 MHz ppc core + }; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x10000000>; // 256MB at 0 + }; + + localbus@80000020 { + compatible = "fsl,mpc5121ads-localbus"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x80000020 0x40>; + + ranges = <0x0 0x0 0xfc000000 0x04000000 + 0x2 0x0 0x82000000 0x00008000>; + + flash@0,0 { + compatible = "cfi-flash"; + reg = <0 0x0 0x4000000>; + bank-width = <4>; + device-width = <1>; + }; + + board-control@2,0 { + compatible = "fsl,mpc5121ads-cpld"; + reg = <0x2 0x0 0x8000>; + }; + }; + + soc@80000000 { + compatible = "fsl,mpc5121-immr"; + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + ranges = <0x0 0x80000000 0x400000>; + reg = <0x80000000 0x400000>; + bus-frequency = <66000000>; // 66 MHz ips bus + + + // IPIC + // interrupts cell = <intr #, sense> + // sense values match linux IORESOURCE_IRQ_* defines: + // sense == 8: Level, low assertion + // sense == 2: Edge, high-to-low change + // + ipic: interrupt-controller@c00 { + compatible = "fsl,mpc5121-ipic", "fsl,ipic"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0xc00 0x100>; + }; + + // 512x PSCs are not 52xx PSCs compatible + // PSC3 serial port A aka ttyPSC0 + serial@11300 { + device_type = "serial"; + compatible = "fsl,mpc5121-psc-uart"; + // Logical port assignment needed until driver + // learns to use aliases + port-number = <0>; + cell-index = <3>; + reg = <0x11300 0x100>; + interrupts = <0x28 0x8>; // actually the fifo irq + interrupt-parent = < &ipic >; + }; + + // PSC4 serial port B aka ttyPSC1 + serial@11400 { + device_type = "serial"; + compatible = "fsl,mpc5121-psc-uart"; + // Logical port assignment needed until driver + // learns to use aliases + port-number = <1>; + cell-index = <4>; + reg = <0x11400 0x100>; + interrupts = <0x28 0x8>; // actually the fifo irq + interrupt-parent = < &ipic >; + }; + + pscsfifo@11f00 { + compatible = "fsl,mpc5121-psc-fifo"; + reg = <0x11f00 0x100>; + interrupts = <0x28 0x8>; + interrupt-parent = < &ipic >; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts index 2d6653fe72f..e1f0dca8ac3 100644 --- a/arch/powerpc/boot/dts/mpc8313erdb.dts +++ b/arch/powerpc/boot/dts/mpc8313erdb.dts @@ -118,6 +118,10 @@ interrupts = <14 0x8>; interrupt-parent = <&ipic>; dfsrr; + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; }; i2c@3100 { diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index b582032ba3d..d7a1ececa30 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -96,7 +96,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; - compatible = "simple-bus"; + compatible = "fsl,mpc8315-immr", "simple-bus"; ranges = <0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts index 7480edae85e..0199c5c548d 100644 --- a/arch/powerpc/boot/dts/mpc834x_mds.dts +++ b/arch/powerpc/boot/dts/mpc834x_mds.dts @@ -332,7 +332,7 @@ 0xc000 0x0 0x0 0x3 &ipic 23 0x8 0xc000 0x0 0x0 0x4 &ipic 20 0x8>; interrupt-parent = <&ipic>; - interrupts = <66 0x8>; + interrupts = <67 0x8>; bus-range = <0 0>; ranges = <0x02000000 0x0 0xb0000000 0xb0000000 0x0 0x10000000 0x42000000 0x0 0xa0000000 0xa0000000 0x0 0x10000000 diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts index 813c259abbe..db37214aee3 100644 --- a/arch/powerpc/boot/dts/mpc8572ds.dts +++ b/arch/powerpc/boot/dts/mpc8572ds.dts @@ -42,6 +42,18 @@ bus-frequency = <0>; clock-frequency = <0>; }; + + PowerPC,8572@1 { + device_type = "cpu"; + reg = <1>; + d-cache-line-size = <20>; // 32 bytes + i-cache-line-size = <20>; // 32 bytes + d-cache-size = <8000>; // L1, 32K + i-cache-size = <8000>; // L1, 32K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + }; }; memory { diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index 8848e637293..d84a012c2aa 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -166,6 +166,7 @@ compatible = "fsl,mpc885-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <0>; reg = <9f0 10>; }; diff --git a/arch/powerpc/boot/dts/sequoia.dts b/arch/powerpc/boot/dts/sequoia.dts index d9046c1adcb..5c13d46f441 100644 --- a/arch/powerpc/boot/dts/sequoia.dts +++ b/arch/powerpc/boot/dts/sequoia.dts @@ -138,6 +138,14 @@ interrupts = <15 8>; }; + USB0: ehci@e0000300 { + compatible = "ibm,usb-ehci-440epx", "usb-ehci"; + interrupt-parent = <&UIC0>; + interrupts = <1a 4>; + reg = <0 e0000300 90 0 e0000390 70>; + big-endian; + }; + POB0: opb { compatible = "ibm,opb-440epx", "ibm,opb"; #address-cells = <1>; diff --git a/arch/powerpc/boot/dts/storcenter.dts b/arch/powerpc/boot/dts/storcenter.dts index 2204874ac5f..5893816c0bc 100644 --- a/arch/powerpc/boot/dts/storcenter.dts +++ b/arch/powerpc/boot/dts/storcenter.dts @@ -15,7 +15,7 @@ / { model = "StorCenter"; - compatible = "storcenter"; + compatible = "iomega,storcenter"; #address-cells = <1>; #size-cells = <1>; @@ -62,12 +62,12 @@ #size-cells = <0>; compatible = "fsl-i2c"; reg = <0x3000 0x100>; - interrupts = <5 2>; + interrupts = <17 2>; interrupt-parent = <&mpic>; rtc@68 { compatible = "dallas,ds1337"; - reg = <68>; + reg = <0x68>; }; }; @@ -78,7 +78,7 @@ reg = <0x4500 0x20>; clock-frequency = <97553800>; /* Hz */ current-speed = <115200>; - interrupts = <9 2>; + interrupts = <25 2>; interrupt-parent = <&mpic>; }; @@ -89,7 +89,7 @@ reg = <0x4600 0x20>; clock-frequency = <97553800>; /* Hz */ current-speed = <9600>; - interrupts = <10 2>; + interrupts = <26 2>; interrupt-parent = <&mpic>; }; @@ -136,6 +136,6 @@ }; chosen { - linux,stdout-path = "/soc/serial@4500"; + linux,stdout-path = &serial0; }; }; diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index 763a0c46f44..c3178155311 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -158,6 +158,29 @@ miboot|uboot) cuboot*) binary=y gzip= + case "$platform" in + *-mpc885ads|*-adder875*|*-ep88xc) + platformo=$object/cuboot-8xx.o + ;; + *5200*|*-motionpro) + platformo=$object/cuboot-52xx.o + ;; + *-pq2fads|*-ep8248e|*-mpc8272*|*-storcenter) + platformo=$object/cuboot-pq2.o + ;; + *-mpc824*) + platformo=$object/cuboot-824x.o + ;; + *-mpc83*) + platformo=$object/cuboot-83xx.o + ;; + *-tqm8541|*-mpc8560*|*-tqm8560|*-tqm8555*) + platformo=$object/cuboot-85xx-cpm2.o + ;; + *-mpc85*) + platformo=$object/cuboot-85xx.o + ;; + esac ;; ps3) platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o" diff --git a/arch/powerpc/configs/mpc83xx_defconfig b/arch/powerpc/configs/mpc83xx_defconfig index 31bdbf3f756..a9807f083bc 100644 --- a/arch/powerpc/configs/mpc83xx_defconfig +++ b/arch/powerpc/configs/mpc83xx_defconfig @@ -186,7 +186,7 @@ CONFIG_PREEMPT_NONE=y # CONFIG_PREEMPT is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set -# CONFIG_MATH_EMULATION is not set +CONFIG_MATH_EMULATION=y CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y CONFIG_ARCH_FLATMEM_ENABLE=y CONFIG_ARCH_POPULATES_NODE_MAP=y @@ -416,14 +416,14 @@ CONFIG_PHYLIB=y # MII PHY device drivers # CONFIG_MARVELL_PHY=y -# CONFIG_DAVICOM_PHY is not set +CONFIG_DAVICOM_PHY=y # CONFIG_QSEMI_PHY is not set # CONFIG_LXT_PHY is not set # CONFIG_CICADA_PHY is not set -# CONFIG_VITESSE_PHY is not set +CONFIG_VITESSE_PHY=y # CONFIG_SMSC_PHY is not set # CONFIG_BROADCOM_PHY is not set -# CONFIG_ICPLUS_PHY is not set +CONFIG_ICPLUS_PHY=y # CONFIG_FIXED_PHY is not set # CONFIG_MDIO_BITBANG is not set CONFIG_NET_ETHERNET=y @@ -436,7 +436,7 @@ CONFIG_MII=y CONFIG_NETDEV_1000=y CONFIG_GIANFAR=y # CONFIG_GFAR_NAPI is not set -# CONFIG_UCC_GETH is not set +CONFIG_UCC_GETH=y CONFIG_NETDEV_10000=y # diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 58dbfeff9b4..0662ae46f72 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -2,6 +2,8 @@ # Makefile for the linux kernel. # +CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' + ifeq ($(CONFIG_PPC64),y) CFLAGS_prom_init.o += -mno-minimal-toc endif @@ -15,7 +17,7 @@ obj-y := semaphore.o cputable.o ptrace.o syscalls.o \ init_task.o process.o systbl.o idle.o \ signal.o obj-y += vdso32/ -obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ +obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \ signal_64.o ptrace32.o \ paca.o cpu_setup_ppc970.o \ cpu_setup_pa6t.o \ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index ed083feaf6f..4b749c41646 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -22,6 +22,7 @@ #include <linux/mman.h> #include <linux/mm.h> #include <linux/suspend.h> +#include <linux/hrtimer.h> #ifdef CONFIG_PPC64 #include <linux/time.h> #include <linux/hardirq.h> @@ -312,7 +313,7 @@ int main(void) DEFINE(CLOCK_REALTIME, CLOCK_REALTIME); DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC); DEFINE(NSEC_PER_SEC, NSEC_PER_SEC); - DEFINE(CLOCK_REALTIME_RES, TICK_NSEC); + DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); #ifdef CONFIG_BUG DEFINE(BUG_ENTRY_SIZE, sizeof(struct bug_entry)); diff --git a/arch/powerpc/kernel/binfmt_elf32.c b/arch/powerpc/kernel/binfmt_elf32.c deleted file mode 100644 index 1d45d7782d4..00000000000 --- a/arch/powerpc/kernel/binfmt_elf32.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * binfmt_elf32.c: Support 32-bit PPC ELF binaries on Power3 and followons. - * based on the SPARC64 version. - * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) - * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) - * - * Copyright (C) 2000,2001 Ken Aaker (kdaaker@rchland.vnet.ibm.com), IBM Corp - * Copyright (C) 2001 Anton Blanchard (anton@au.ibm.com), IBM - * - * 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 <asm/processor.h> -#include <linux/module.h> -#include <linux/compat.h> -#include <linux/elfcore-compat.h> - -#undef ELF_ARCH -#undef ELF_CLASS -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PPC - -#undef elfhdr -#undef elf_phdr -#undef elf_note -#undef elf_addr_t -#define elfhdr elf32_hdr -#define elf_phdr elf32_phdr -#define elf_note elf32_note -#define elf_addr_t Elf32_Off - -#define elf_prstatus compat_elf_prstatus -#define elf_prpsinfo compat_elf_prpsinfo - -#define elf_core_copy_regs compat_elf_core_copy_regs -static inline void compat_elf_core_copy_regs(compat_elf_gregset_t *elf_regs, - struct pt_regs *regs) -{ - PPC_ELF_CORE_COPY_REGS((*elf_regs), regs); -} - -#define elf_core_copy_task_regs compat_elf_core_copy_task_regs -static int compat_elf_core_copy_task_regs(struct task_struct *tsk, - compat_elf_gregset_t *elf_regs) -{ - struct pt_regs *regs = tsk->thread.regs; - if (regs) - compat_elf_core_copy_regs(elf_regs, regs); - return 1; -} - -#include <linux/time.h> - -#undef cputime_to_timeval -#define cputime_to_timeval cputime_to_compat_timeval -static __inline__ void -cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) -{ - unsigned long jiffies = cputime_to_jiffies(cputime); - value->tv_usec = (jiffies % HZ) * (1000000L / HZ); - value->tv_sec = jiffies / HZ; -} - -#define init_elf_binfmt init_elf32_binfmt - -#include "../../../fs/binfmt_elf.c" diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index a4c2771b5e6..2a8f5cc5184 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -959,6 +959,9 @@ static struct cpu_spec __initdata cpu_specs[] = { .icache_bsize = 32, .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, + .num_pmcs = 4, + .oprofile_cpu_type = "ppc/e300", + .oprofile_type = PPC_OPROFILE_FSL_EMB, .platform = "ppc603", }, { /* e300c4 (e300c1, plus one IU) */ @@ -971,6 +974,9 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, .machine_check = machine_check_generic, + .num_pmcs = 4, + .oprofile_cpu_type = "ppc/e300", + .oprofile_type = PPC_OPROFILE_FSL_EMB, .platform = "ppc603", }, { /* default match, we assume split I/D cache & TB (non-601)... */ @@ -1435,7 +1441,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", - .oprofile_type = PPC_OPROFILE_BOOKE, + .oprofile_type = PPC_OPROFILE_FSL_EMB, .machine_check = machine_check_e500, .platform = "ppc8540", }, @@ -1453,7 +1459,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", - .oprofile_type = PPC_OPROFILE_BOOKE, + .oprofile_type = PPC_OPROFILE_FSL_EMB, .machine_check = machine_check_e500, .platform = "ppc8548", }, diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 76b862bd1fe..61dd17449dd 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -36,7 +36,8 @@ static struct legacy_serial_info { static struct __initdata of_device_id parents[] = { {.type = "soc",}, {.type = "tsi-bridge",}, - {.type = "opb", .compatible = "ibm,opb",}, + {.type = "opb", }, + {.compatible = "ibm,opb",}, {.compatible = "simple-bus",}, {.compatible = "wrs,epld-localbus",}, }; diff --git a/arch/powerpc/kernel/pmc.c b/arch/powerpc/kernel/pmc.c index ea04e0ab3f2..0516e2d3e02 100644 --- a/arch/powerpc/kernel/pmc.c +++ b/arch/powerpc/kernel/pmc.c @@ -26,7 +26,7 @@ static void dummy_perf(struct pt_regs *regs) { -#if defined(CONFIG_FSL_BOOKE) && !defined(CONFIG_E200) +#if defined(CONFIG_FSL_EMB_PERFMON) mtpmr(PMRN_PMGC0, mfpmr(PMRN_PMGC0) & ~PMGC0_PMIE); #elif defined(CONFIG_PPC64) || defined(CONFIG_6xx) if (cur_cpu_spec->pmc_type == PPC_PMC_IBM) diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 8b056d2295c..7673e986573 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -21,6 +21,8 @@ #include <linux/smp.h> #include <linux/errno.h> #include <linux/ptrace.h> +#include <linux/regset.h> +#include <linux/elf.h> #include <linux/user.h> #include <linux/security.h> #include <linux/signal.h> @@ -58,20 +60,38 @@ #define PT_MAX_PUT_REG PT_CCR #endif +static unsigned long get_user_msr(struct task_struct *task) +{ + return task->thread.regs->msr | task->thread.fpexc_mode; +} + +static int set_user_msr(struct task_struct *task, unsigned long msr) +{ + task->thread.regs->msr &= ~MSR_DEBUGCHANGE; + task->thread.regs->msr |= msr & MSR_DEBUGCHANGE; + return 0; +} + +/* + * We prevent mucking around with the reserved area of trap + * which are used internally by the kernel. + */ +static int set_user_trap(struct task_struct *task, unsigned long trap) +{ + task->thread.regs->trap = trap & 0xfff0; + return 0; +} + /* * Get contents of register REGNO in task TASK. */ unsigned long ptrace_get_reg(struct task_struct *task, int regno) { - unsigned long tmp = 0; - if (task->thread.regs == NULL) return -EIO; - if (regno == PT_MSR) { - tmp = ((unsigned long *)task->thread.regs)[PT_MSR]; - return tmp | task->thread.fpexc_mode; - } + if (regno == PT_MSR) + return get_user_msr(task); if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) return ((unsigned long *)task->thread.regs)[regno]; @@ -87,40 +107,134 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) if (task->thread.regs == NULL) return -EIO; - if (regno <= PT_MAX_PUT_REG || regno == PT_TRAP) { - if (regno == PT_MSR) - data = (data & MSR_DEBUGCHANGE) - | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); - /* We prevent mucking around with the reserved area of trap - * which are used internally by the kernel - */ - if (regno == PT_TRAP) - data &= 0xfff0; + if (regno == PT_MSR) + return set_user_msr(task, data); + if (regno == PT_TRAP) + return set_user_trap(task, data); + + if (regno <= PT_MAX_PUT_REG) { ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -EIO; } +static int gpr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + target->thread.regs, + 0, offsetof(struct pt_regs, msr)); + if (!ret) { + unsigned long msr = get_user_msr(target); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr, + offsetof(struct pt_regs, msr), + offsetof(struct pt_regs, msr) + + sizeof(msr)); + } + + BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != + offsetof(struct pt_regs, msr) + sizeof(long)); + + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.regs->orig_gpr3, + offsetof(struct pt_regs, orig_gpr3), + sizeof(struct pt_regs)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(struct pt_regs), -1); + + return ret; +} + +static int gpr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long reg; + int ret; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + target->thread.regs, + 0, PT_MSR * sizeof(reg)); -static int get_fpregs(void __user *data, struct task_struct *task, - int has_fpscr) + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, + PT_MSR * sizeof(reg), + (PT_MSR + 1) * sizeof(reg)); + if (!ret) + ret = set_user_msr(target, reg); + } + + BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != + offsetof(struct pt_regs, msr) + sizeof(long)); + + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.regs->orig_gpr3, + PT_ORIG_R3 * sizeof(reg), + (PT_MAX_PUT_REG + 1) * sizeof(reg)); + + if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret) + ret = user_regset_copyin_ignore( + &pos, &count, &kbuf, &ubuf, + (PT_MAX_PUT_REG + 1) * sizeof(reg), + PT_TRAP * sizeof(reg)); + + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, + PT_TRAP * sizeof(reg), + (PT_TRAP + 1) * sizeof(reg)); + if (!ret) + ret = set_user_trap(target, reg); + } + + if (!ret) + ret = user_regset_copyin_ignore( + &pos, &count, &kbuf, &ubuf, + (PT_TRAP + 1) * sizeof(reg), -1); + + return ret; +} + +static int fpr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - unsigned int count = has_fpscr ? 33 : 32; + flush_fp_to_thread(target); - if (copy_to_user(data, task->thread.fpr, count * sizeof(double))) - return -EFAULT; - return 0; + BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != + offsetof(struct thread_struct, fpr[32])); + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpr, 0, -1); } -static int set_fpregs(void __user *data, struct task_struct *task, - int has_fpscr) +static int fpr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - unsigned int count = has_fpscr ? 33 : 32; + flush_fp_to_thread(target); - if (copy_from_user(task->thread.fpr, data, count * sizeof(double))) - return -EFAULT; - return 0; + BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != + offsetof(struct thread_struct, fpr[32])); + + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpr, 0, -1); } @@ -138,56 +252,74 @@ static int set_fpregs(void __user *data, struct task_struct *task, * (combined (32- and 64-bit) gdb. */ -/* - * Get contents of AltiVec register state in task TASK - */ -static int get_vrregs(unsigned long __user *data, struct task_struct *task) +static int vr_active(struct task_struct *target, + const struct user_regset *regset) { - unsigned long regsize; + flush_altivec_to_thread(target); + return target->thread.used_vr ? regset->n : 0; +} - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_to_user(data, task->thread.vr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); +static int vr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_to_user(data, &task->thread.vscr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + flush_altivec_to_thread(target); - /* copy VRSAVE */ - if (put_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; + BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != + offsetof(struct thread_struct, vr[32])); - return 0; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.vr, 0, + 33 * sizeof(vector128)); + if (!ret) { + /* + * Copy out only the low-order word of vrsave. + */ + union { + elf_vrreg_t reg; + u32 word; + } vrsave; + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.vrsave; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave, + 33 * sizeof(vector128), -1); + } + + return ret; } -/* - * Write contents of AltiVec register state into task TASK. - */ -static int set_vrregs(struct task_struct *task, unsigned long __user *data) +static int vr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - unsigned long regsize; + int ret; - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_from_user(task->thread.vr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + flush_altivec_to_thread(target); - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_from_user(&task->thread.vscr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != + offsetof(struct thread_struct, vr[32])); - /* copy VRSAVE */ - if (get_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.vr, 0, 33 * sizeof(vector128)); + if (!ret && count > 0) { + /* + * We use only the first word of vrsave. + */ + union { + elf_vrreg_t reg; + u32 word; + } vrsave; + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.vrsave; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave, + 33 * sizeof(vector128), -1); + if (!ret) + target->thread.vrsave = vrsave.word; + } - return 0; + return ret; } #endif /* CONFIG_ALTIVEC */ @@ -203,57 +335,273 @@ static int set_vrregs(struct task_struct *task, unsigned long __user *data) * } */ -/* - * Get contents of SPE register state in task TASK. - */ -static int get_evrregs(unsigned long *data, struct task_struct *task) +static int evr_active(struct task_struct *target, + const struct user_regset *regset) { - int i; + flush_spe_to_thread(target); + return target->thread.used_spe ? regset->n : 0; +} - if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(unsigned long))) - return -EFAULT; +static int evr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; - /* copy SPEFSCR */ - if (__put_user(task->thread.spefscr, &data[34])) - return -EFAULT; + flush_spe_to_thread(target); - /* copy SPE registers EVR[0] .. EVR[31] */ - for (i = 0; i < 32; i++, data++) - if (__put_user(task->thread.evr[i], data)) - return -EFAULT; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.evr, + 0, sizeof(target->thread.evr)); - /* copy ACC */ - if (__put_user64(task->thread.acc, (unsigned long long *)data)) - return -EFAULT; + BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) != + offsetof(struct thread_struct, spefscr)); - return 0; + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.acc, + sizeof(target->thread.evr), -1); + + return ret; +} + +static int evr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + + flush_spe_to_thread(target); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.evr, + 0, sizeof(target->thread.evr)); + + BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) != + offsetof(struct thread_struct, spefscr)); + + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.acc, + sizeof(target->thread.evr), -1); + + return ret; } +#endif /* CONFIG_SPE */ + /* - * Write contents of SPE register state into task TASK. + * These are our native regset flavors. */ -static int set_evrregs(struct task_struct *task, unsigned long *data) +enum powerpc_regset { + REGSET_GPR, + REGSET_FPR, +#ifdef CONFIG_ALTIVEC + REGSET_VMX, +#endif +#ifdef CONFIG_SPE + REGSET_SPE, +#endif +}; + +static const struct user_regset native_regsets[] = { + [REGSET_GPR] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(long), .align = sizeof(long), + .get = gpr_get, .set = gpr_set + }, + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(double), .align = sizeof(double), + .get = fpr_get, .set = fpr_set + }, +#ifdef CONFIG_ALTIVEC + [REGSET_VMX] = { + .core_note_type = NT_PPC_VMX, .n = 34, + .size = sizeof(vector128), .align = sizeof(vector128), + .active = vr_active, .get = vr_get, .set = vr_set + }, +#endif +#ifdef CONFIG_SPE + [REGSET_SPE] = { + .n = 35, + .size = sizeof(u32), .align = sizeof(u32), + .active = evr_active, .get = evr_get, .set = evr_set + }, +#endif +}; + +static const struct user_regset_view user_ppc_native_view = { + .name = UTS_MACHINE, .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI, + .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) +}; + +#ifdef CONFIG_PPC64 +#include <linux/compat.h> + +static int gpr32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - int i; + const unsigned long *regs = &target->thread.regs->gpr[0]; + compat_ulong_t *k = kbuf; + compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); - if (!access_ok(VERIFY_READ, data, 35 * sizeof(unsigned long))) - return -EFAULT; + pos /= sizeof(reg); + count /= sizeof(reg); - /* copy SPEFSCR */ - if (__get_user(task->thread.spefscr, &data[34])) - return -EFAULT; + if (kbuf) + for (; count > 0 && pos < PT_MSR; --count) + *k++ = regs[pos++]; + else + for (; count > 0 && pos < PT_MSR; --count) + if (__put_user((compat_ulong_t) regs[pos++], u++)) + return -EFAULT; - /* copy SPE registers EVR[0] .. EVR[31] */ - for (i = 0; i < 32; i++, data++) - if (__get_user(task->thread.evr[i], data)) + if (count > 0 && pos == PT_MSR) { + reg = get_user_msr(target); + if (kbuf) + *k++ = reg; + else if (__put_user(reg, u++)) return -EFAULT; - /* copy ACC */ - if (__get_user64(task->thread.acc, (unsigned long long*)data)) - return -EFAULT; + ++pos; + --count; + } - return 0; + if (kbuf) + for (; count > 0 && pos < PT_REGS_COUNT; --count) + *k++ = regs[pos++]; + else + for (; count > 0 && pos < PT_REGS_COUNT; --count) + if (__put_user((compat_ulong_t) regs[pos++], u++)) + return -EFAULT; + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + PT_REGS_COUNT * sizeof(reg), -1); +} + +static int gpr32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *regs = &target->thread.regs->gpr[0]; + const compat_ulong_t *k = kbuf; + const compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) + for (; count > 0 && pos < PT_MSR; --count) + regs[pos++] = *k++; + else + for (; count > 0 && pos < PT_MSR; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + regs[pos++] = reg; + } + + + if (count > 0 && pos == PT_MSR) { + if (kbuf) + reg = *k++; + else if (__get_user(reg, u++)) + return -EFAULT; + set_user_msr(target, reg); + ++pos; + --count; + } + + if (kbuf) + for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) + regs[pos++] = *k++; + else + for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + regs[pos++] = reg; + } + + if (count > 0 && pos == PT_TRAP) { + if (kbuf) + reg = *k++; + else if (__get_user(reg, u++)) + return -EFAULT; + set_user_trap(target, reg); + ++pos; + --count; + } + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + (PT_TRAP + 1) * sizeof(reg), -1); +} + +/* + * These are the regset flavors matching the CONFIG_PPC32 native set. + */ +static const struct user_regset compat_regsets[] = { + [REGSET_GPR] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), + .get = gpr32_get, .set = gpr32_set + }, + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(double), .align = sizeof(double), + .get = fpr_get, .set = fpr_set + }, +#ifdef CONFIG_ALTIVEC + [REGSET_VMX] = { + .core_note_type = NT_PPC_VMX, .n = 34, + .size = sizeof(vector128), .align = sizeof(vector128), + .active = vr_active, .get = vr_get, .set = vr_set + }, +#endif +#ifdef CONFIG_SPE + [REGSET_SPE] = { + .core_note_type = NT_PPC_SPE, .n = 35, + .size = sizeof(u32), .align = sizeof(u32), + .active = evr_active, .get = evr_get, .set = evr_set + }, +#endif +}; + +static const struct user_regset_view user_ppc_compat_view = { + .name = "ppc", .e_machine = EM_PPC, .ei_osabi = ELF_OSABI, + .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets) +}; +#endif /* CONFIG_PPC64 */ + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ +#ifdef CONFIG_PPC64 + if (test_tsk_thread_flag(task, TIF_32BIT)) + return &user_ppc_compat_view; +#endif + return &user_ppc_native_view; } -#endif /* CONFIG_SPE */ void user_enable_single_step(struct task_struct *task) @@ -323,55 +671,29 @@ void ptrace_disable(struct task_struct *child) static long arch_ptrace_old(struct task_struct *child, long request, long addr, long data) { - int ret = -EPERM; - - switch(request) { - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ - flush_fp_to_thread(child); - ret = get_fpregs((void __user *)addr, child, 0); - break; - } - - case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ - flush_fp_to_thread(child); - ret = set_fpregs((void __user *)addr, child, 0); - break; + switch (request) { + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_GPR, 0, 32 * sizeof(long), + (void __user *) data); + + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_GPR, 0, 32 * sizeof(long), + (const void __user *) data); + + case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_FPR, 0, 32 * sizeof(double), + (void __user *) data); + + case PPC_PTRACE_SETFPREGS: /* Set FPRs 0 - 31. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_FPR, 0, 32 * sizeof(double), + (const void __user *) data); } - } - return ret; + return -EPERM; } long arch_ptrace(struct task_struct *child, long request, long addr, long data) @@ -379,12 +701,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) int ret = -EPERM; switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: - ret = generic_ptrace_peekdata(child, addr, data); - break; - /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long index, tmp; @@ -412,12 +728,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = generic_ptrace_pokedata(child, addr, data); - break; - /* write the word at location addr in the USER area */ case PTRACE_POKEUSR: { unsigned long index; @@ -462,85 +772,60 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #ifdef CONFIG_PPC64 case PTRACE_GETREGS64: #endif - case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ - int ui; - if (!access_ok(VERIFY_WRITE, (void __user *)data, - sizeof(struct pt_regs))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret |= __put_user(ptrace_get_reg(child, ui), - (unsigned long __user *) data); - data += sizeof(long); - } - break; - } + case PTRACE_GETREGS: /* Get all pt_regs from the child. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_GPR, + 0, sizeof(struct pt_regs), + (void __user *) data); #ifdef CONFIG_PPC64 case PTRACE_SETREGS64: #endif - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - unsigned long tmp; - int ui; - if (!access_ok(VERIFY_READ, (void __user *)data, - sizeof(struct pt_regs))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret = __get_user(tmp, (unsigned long __user *) data); - if (ret) - break; - ptrace_put_reg(child, ui, tmp); - data += sizeof(long); - } - break; - } - - case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */ - flush_fp_to_thread(child); - ret = get_fpregs((void __user *)data, child, 1); - break; - } - - case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */ - flush_fp_to_thread(child); - ret = set_fpregs((void __user *)data, child, 1); - break; - } + case PTRACE_SETREGS: /* Set all gp regs in the child. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_GPR, + 0, sizeof(struct pt_regs), + (const void __user *) data); + + case PTRACE_GETFPREGS: /* Get the child FPU state (FPR0...31 + FPSCR) */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_FPR, + 0, sizeof(elf_fpregset_t), + (void __user *) data); + + case PTRACE_SETFPREGS: /* Set the child FPU state (FPR0...31 + FPSCR) */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_FPR, + 0, sizeof(elf_fpregset_t), + (const void __user *) data); #ifdef CONFIG_ALTIVEC case PTRACE_GETVRREGS: - /* Get the child altivec register state. */ - flush_altivec_to_thread(child); - ret = get_vrregs((unsigned long __user *)data, child); - break; + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_VMX, + 0, (33 * sizeof(vector128) + + sizeof(u32)), + (void __user *) data); case PTRACE_SETVRREGS: - /* Set the child altivec register state. */ - flush_altivec_to_thread(child); - ret = set_vrregs(child, (unsigned long __user *)data); - break; + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_VMX, + 0, (33 * sizeof(vector128) + + sizeof(u32)), + (const void __user *) data); #endif #ifdef CONFIG_SPE case PTRACE_GETEVRREGS: /* Get the child spe register state. */ - flush_spe_to_thread(child); - ret = get_evrregs((unsigned long __user *)data, child); - break; + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_SPE, 0, 35 * sizeof(u32), + (void __user *) data); case PTRACE_SETEVRREGS: /* Set the child spe register state. */ - /* this is to clear the MSR_SPE bit to force a reload - * of register state from memory */ - flush_spe_to_thread(child); - ret = set_evrregs(child, (unsigned long __user *)data); - break; + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_SPE, 0, 35 * sizeof(u32), + (const void __user *) data); #endif /* Old reverse args ptrace callss */ diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index fea6206ff90..4c1de6af4c0 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -24,9 +24,11 @@ #include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/ptrace.h> +#include <linux/regset.h> #include <linux/user.h> #include <linux/security.h> #include <linux/signal.h> +#include <linux/compat.h> #include <asm/uaccess.h> #include <asm/page.h> @@ -45,87 +47,31 @@ static long compat_ptrace_old(struct task_struct *child, long request, long addr, long data) { - int ret = -EPERM; - - switch(request) { - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; + switch (request) { + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + return copy_regset_to_user(child, + task_user_regset_view(current), 0, + 0, 32 * sizeof(compat_long_t), + compat_ptr(data)); + + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + return copy_regset_from_user(child, + task_user_regset_view(current), 0, + 0, 32 * sizeof(compat_long_t), + compat_ptr(data)); } - } - return ret; + return -EPERM; } -long compat_sys_ptrace(int request, int pid, unsigned long addr, - unsigned long data) +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) { - struct task_struct *child; + unsigned long addr = caddr; + unsigned long data = cdata; int ret; - lock_kernel(); - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - goto out; - } - - child = ptrace_get_task_struct(pid); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - goto out; - } - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned int tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp, (u32 __user *)data); - break; - } - /* * Read 4 bytes of the other process' storage * data is a pointer specifying where the user wants the @@ -225,19 +171,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: { - unsigned int tmp; - tmp = data; - ret = 0; - if (access_process_vm(child, addr, &tmp, sizeof(tmp), 1) - == sizeof(tmp)) - break; - ret = -EIO; - break; - } - /* * Write 4 bytes into the other process' storage * data is the 4 bytes that the user wants written @@ -337,46 +270,17 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - case PTRACE_GETEVENTMSG: - ret = put_user(child->ptrace_message, (unsigned int __user *) data); - break; + case PTRACE_GETREGS: /* Get all pt_regs from the child. */ + return copy_regset_to_user( + child, task_user_regset_view(current), 0, + 0, PT_REGS_COUNT * sizeof(compat_long_t), + compat_ptr(data)); - case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ - int ui; - if (!access_ok(VERIFY_WRITE, (void __user *)data, - PT_REGS_COUNT * sizeof(int))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret |= __put_user(ptrace_get_reg(child, ui), - (unsigned int __user *) data); - data += sizeof(int); - } - break; - } - - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - unsigned long tmp; - int ui; - if (!access_ok(VERIFY_READ, (void __user *)data, - PT_REGS_COUNT * sizeof(int))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret = __get_user(tmp, (unsigned int __user *) data); - if (ret) - break; - ptrace_put_reg(child, ui, tmp); - data += sizeof(int); - } - break; - } + case PTRACE_SETREGS: /* Set all gp regs in the child. */ + return copy_regset_from_user( + child, task_user_regset_view(current), 0, + 0, PT_REGS_COUNT * sizeof(compat_long_t), + compat_ptr(data)); case PTRACE_GETFPREGS: case PTRACE_SETFPREGS: @@ -402,12 +306,9 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; default: - ret = ptrace_request(child, request, addr, data); + ret = compat_ptrace_request(child, request, addr, data); break; } -out_tsk: - put_task_struct(child); -out: - unlock_kernel(); + return ret; } diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 848a20475db..4b5b7ff4f78 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -54,7 +54,7 @@ #endif #include <asm/kexec.h> -#ifdef CONFIG_DEBUGGER +#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) int (*__debugger)(struct pt_regs *regs); int (*__debugger_ipi)(struct pt_regs *regs); int (*__debugger_bpt)(struct pt_regs *regs); diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index f0bad7070fb..f98867252ee 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -176,7 +176,7 @@ static void __devinit vio_dev_release(struct device *dev) * Returns a pointer to the created vio_dev or NULL if node has * NULL device_type or compatible fields. */ -struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node) +struct vio_dev *vio_register_device_node(struct device_node *of_node) { struct vio_dev *viodev; const unsigned int *unit_address; diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index e8122447f01..be5c506779a 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -129,6 +129,39 @@ int __devinit arch_add_memory(int nid, u64 start, u64 size) return __add_pages(zone, start_pfn, nr_pages); } +#ifdef CONFIG_MEMORY_HOTREMOVE +int remove_memory(u64 start, u64 size) +{ + unsigned long start_pfn, end_pfn; + int ret; + + start_pfn = start >> PAGE_SHIFT; + end_pfn = start_pfn + (size >> PAGE_SHIFT); + ret = offline_pages(start_pfn, end_pfn, 120 * HZ); + if (ret) + goto out; + /* Arch-specific calls go here - next patch */ +out: + return ret; +} +#endif /* CONFIG_MEMORY_HOTREMOVE */ + +/* + * walk_memory_resource() needs to make sure there is no holes in a given + * memory range. On PPC64, since this range comes from /sysfs, the range + * is guaranteed to be valid, non-overlapping and can not contain any + * holes. By the time we get here (memory add or remove), /proc/device-tree + * is updated and correct. Only reason we need to check against device-tree + * would be if we allow user-land to specify a memory range through a + * system call/ioctl etc. instead of doing offline/online through /sysfs. + */ +int +walk_memory_resource(unsigned long start_pfn, unsigned long nr_pages, void *arg, + int (*func)(unsigned long, unsigned long, void *)) +{ + return (*func)(start_pfn, nr_pages, arg); +} + #endif /* CONFIG_MEMORY_HOTPLUG */ void show_mem(void) @@ -220,12 +253,13 @@ void __init do_init_bootmem(void) lmb_size_bytes(&lmb.reserved, i) - 1; if (addr < total_lowmem) reserve_bootmem(lmb.reserved.region[i].base, - lmb_size_bytes(&lmb.reserved, i)); + lmb_size_bytes(&lmb.reserved, i), + BOOTMEM_DEFAULT); else if (lmb.reserved.region[i].base < total_lowmem) { unsigned long adjusted_size = total_lowmem - lmb.reserved.region[i].base; reserve_bootmem(lmb.reserved.region[i].base, - adjusted_size); + adjusted_size, BOOTMEM_DEFAULT); } } #else @@ -234,7 +268,8 @@ void __init do_init_bootmem(void) /* reserve the sections we're already using */ for (i = 0; i < lmb.reserved.cnt; i++) reserve_bootmem(lmb.reserved.region[i].base, - lmb_size_bytes(&lmb.reserved, i)); + lmb_size_bytes(&lmb.reserved, i), + BOOTMEM_DEFAULT); #endif /* XXX need to clip this if using highmem? */ @@ -483,7 +518,12 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, */ _tlbie(address, 0 /* 8xx doesn't care about PID */); #endif - if (!PageReserved(page) + /* The _PAGE_USER test should really be _PAGE_EXEC, but + * older glibc versions execute some code from no-exec + * pages, which for now we are supporting. If exec-only + * pages are ever implemented, this will have to change. + */ + if (!PageReserved(page) && (pte_val(pte) & _PAGE_USER) && !test_bit(PG_arch_1, &page->flags)) { if (vma->vm_mm == current->active_mm) { __flush_dcache_icache((void *) address); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index c12adc3ddff..a300d254aac 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -24,6 +24,8 @@ static int numa_enabled = 1; +static char *cmdline __initdata; + static int numa_debug; #define dbg(args...) if (numa_debug) { printk(KERN_INFO args); } @@ -39,6 +41,53 @@ static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES]; static int min_common_depth; static int n_mem_addr_cells, n_mem_size_cells; +static int __cpuinit fake_numa_create_new_node(unsigned long end_pfn, + unsigned int *nid) +{ + unsigned long long mem; + char *p = cmdline; + static unsigned int fake_nid; + static unsigned long long curr_boundary; + + /* + * Modify node id, iff we started creating NUMA nodes + * We want to continue from where we left of the last time + */ + if (fake_nid) + *nid = fake_nid; + /* + * In case there are no more arguments to parse, the + * node_id should be the same as the last fake node id + * (we've handled this above). + */ + if (!p) + return 0; + + mem = memparse(p, &p); + if (!mem) + return 0; + + if (mem < curr_boundary) + return 0; + + curr_boundary = mem; + + if ((end_pfn << PAGE_SHIFT) > mem) { + /* + * Skip commas and spaces + */ + while (*p == ',' || *p == ' ' || *p == '\t') + p++; + + cmdline = p; + fake_nid++; + *nid = fake_nid; + dbg("created new fake_node with id %d\n", fake_nid); + return 1; + } + return 0; +} + static void __cpuinit map_cpu_to_node(int cpu, int node) { numa_cpu_lookup_table[cpu] = node; @@ -344,6 +393,9 @@ static void __init parse_drconf_memory(struct device_node *memory) if (nid == 0xffff || nid >= MAX_NUMNODES) nid = default_nid; } + + fake_numa_create_new_node(((start + lmb_size) >> PAGE_SHIFT), + &nid); node_set_online(nid); size = numa_enforce_memory_limit(start, lmb_size); @@ -429,6 +481,8 @@ new_range: nid = of_node_to_nid_single(memory); if (nid < 0) nid = default_nid; + + fake_numa_create_new_node(((start + size) >> PAGE_SHIFT), &nid); node_set_online(nid); if (!(size = numa_enforce_memory_limit(start, size))) { @@ -461,7 +515,7 @@ static void __init setup_nonnuma(void) unsigned long top_of_ram = lmb_end_of_DRAM(); unsigned long total_ram = lmb_phys_mem_size(); unsigned long start_pfn, end_pfn; - unsigned int i; + unsigned int i, nid = 0; printk(KERN_DEBUG "Top of RAM: 0x%lx, Total RAM: 0x%lx\n", top_of_ram, total_ram); @@ -471,9 +525,11 @@ static void __init setup_nonnuma(void) for (i = 0; i < lmb.memory.cnt; ++i) { start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT; end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i); - add_active_range(0, start_pfn, end_pfn); + + fake_numa_create_new_node(end_pfn, &nid); + add_active_range(nid, start_pfn, end_pfn); + node_set_online(nid); } - node_set_online(0); } void __init dump_numa_cpu_topology(void) @@ -675,7 +731,7 @@ void __init do_init_bootmem(void) dbg("reserve_bootmem %lx %lx\n", physbase, size); reserve_bootmem_node(NODE_DATA(nid), physbase, - size); + size, BOOTMEM_DEFAULT); } } @@ -702,6 +758,10 @@ static int __init early_numa(char *p) if (strstr(p, "debug")) numa_debug = 1; + p = strstr(p, "fake="); + if (p) + cmdline = p + strlen("fake="); + return 0; } early_param("numa", early_numa); diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index f80f90c4d58..ac3390f8190 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -107,19 +107,20 @@ __init_refok pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long add return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *ptepage; #ifdef CONFIG_HIGHPTE - gfp_t flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT; + gfp_t flags = GFP_KERNEL | __GFP_HIGHMEM | __GFP_REPEAT | __GFP_ZERO; #else - gfp_t flags = GFP_KERNEL | __GFP_REPEAT; + gfp_t flags = GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO; #endif ptepage = alloc_pages(flags, 0); - if (ptepage) - clear_highpage(ptepage); + if (!ptepage) + return NULL; + pgtable_page_ctor(ptepage); return ptepage; } @@ -131,11 +132,12 @@ void pte_free_kernel(struct mm_struct *mm, pte_t *pte) free_page((unsigned long)pte); } -void pte_free(struct mm_struct *mm, struct page *ptepage) +void pte_free(struct mm_struct *mm, pgtable_t ptepage) { #ifdef CONFIG_SMP hash_page_sync(); #endif + pgtable_page_dtor(ptepage); __free_page(ptepage); } diff --git a/arch/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile index c5f64c3bd66..2ef6b0dddd8 100644 --- a/arch/powerpc/oprofile/Makefile +++ b/arch/powerpc/oprofile/Makefile @@ -15,5 +15,5 @@ oprofile-$(CONFIG_OPROFILE_CELL) += op_model_cell.o \ cell/spu_profiler.o cell/vma_map.o \ cell/spu_task_sync.o oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o -oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o +oprofile-$(CONFIG_FSL_EMB_PERFMON) += op_model_fsl_emb.o oprofile-$(CONFIG_6xx) += op_model_7450.o diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c index a28cce1d6c2..4908dc98f9c 100644 --- a/arch/powerpc/oprofile/common.c +++ b/arch/powerpc/oprofile/common.c @@ -202,9 +202,9 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) model = &op_model_7450; break; #endif -#ifdef CONFIG_FSL_BOOKE - case PPC_OPROFILE_BOOKE: - model = &op_model_fsl_booke; +#if defined(CONFIG_FSL_EMB_PERFMON) + case PPC_OPROFILE_FSL_EMB: + model = &op_model_fsl_emb; break; #endif default: diff --git a/arch/powerpc/oprofile/op_model_fsl_booke.c b/arch/powerpc/oprofile/op_model_fsl_emb.c index 183a28bb181..91596f6ba1f 100644 --- a/arch/powerpc/oprofile/op_model_fsl_booke.c +++ b/arch/powerpc/oprofile/op_model_fsl_emb.c @@ -1,7 +1,5 @@ /* - * arch/powerpc/oprofile/op_model_fsl_booke.c - * - * Freescale Book-E oprofile support, based on ppc64 oprofile support + * Freescale Embedded oprofile support, based on ppc64 oprofile support * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM * * Copyright (c) 2004 Freescale Semiconductor, Inc @@ -22,7 +20,7 @@ #include <asm/system.h> #include <asm/processor.h> #include <asm/cputable.h> -#include <asm/reg_booke.h> +#include <asm/reg_fsl_emb.h> #include <asm/page.h> #include <asm/pmc.h> #include <asm/oprofile_impl.h> @@ -244,7 +242,7 @@ static void dump_pmcs(void) mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3)); } -static int fsl_booke_cpu_setup(struct op_counter_config *ctr) +static int fsl_emb_cpu_setup(struct op_counter_config *ctr) { int i; @@ -262,7 +260,7 @@ static int fsl_booke_cpu_setup(struct op_counter_config *ctr) return 0; } -static int fsl_booke_reg_setup(struct op_counter_config *ctr, +static int fsl_emb_reg_setup(struct op_counter_config *ctr, struct op_system_config *sys, int num_ctrs) { @@ -281,7 +279,7 @@ static int fsl_booke_reg_setup(struct op_counter_config *ctr, return 0; } -static int fsl_booke_start(struct op_counter_config *ctr) +static int fsl_emb_start(struct op_counter_config *ctr) { int i; @@ -315,7 +313,7 @@ static int fsl_booke_start(struct op_counter_config *ctr) return 0; } -static void fsl_booke_stop(void) +static void fsl_emb_stop(void) { /* freeze counters */ pmc_stop_ctrs(); @@ -329,7 +327,7 @@ static void fsl_booke_stop(void) } -static void fsl_booke_handle_interrupt(struct pt_regs *regs, +static void fsl_emb_handle_interrupt(struct pt_regs *regs, struct op_counter_config *ctr) { unsigned long pc; @@ -362,10 +360,10 @@ static void fsl_booke_handle_interrupt(struct pt_regs *regs, pmc_start_ctrs(1); } -struct op_powerpc_model op_model_fsl_booke = { - .reg_setup = fsl_booke_reg_setup, - .cpu_setup = fsl_booke_cpu_setup, - .start = fsl_booke_start, - .stop = fsl_booke_stop, - .handle_interrupt = fsl_booke_handle_interrupt, +struct op_powerpc_model op_model_fsl_emb = { + .reg_setup = fsl_emb_reg_setup, + .cpu_setup = fsl_emb_cpu_setup, + .start = fsl_emb_start, + .stop = fsl_emb_stop, + .handle_interrupt = fsl_emb_handle_interrupt, }; diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig index 74f31177e47..a9260e21451 100644 --- a/arch/powerpc/platforms/40x/Kconfig +++ b/arch/powerpc/platforms/40x/Kconfig @@ -72,6 +72,7 @@ config WALNUT default y select 405GP select PCI + select OF_RTC help This option enables support for the IBM PPC405GP evaluation board. diff --git a/arch/powerpc/platforms/40x/virtex.c b/arch/powerpc/platforms/40x/virtex.c index 88b66444dfb..0422590040d 100644 --- a/arch/powerpc/platforms/40x/virtex.c +++ b/arch/powerpc/platforms/40x/virtex.c @@ -37,7 +37,7 @@ static int __init virtex_probe(void) { unsigned long root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(root, "xilinx,virtex")) + if (!of_flat_dt_is_compatible(root, "xlnx,virtex")) return 0; return 1; diff --git a/arch/powerpc/platforms/40x/walnut.c b/arch/powerpc/platforms/40x/walnut.c index 5d9edd917f9..b8b257efeb7 100644 --- a/arch/powerpc/platforms/40x/walnut.c +++ b/arch/powerpc/platforms/40x/walnut.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/of_platform.h> +#include <linux/rtc.h> #include <asm/machdep.h> #include <asm/prom.h> diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c index 8f01563dbd2..da5b7b7599d 100644 --- a/arch/powerpc/platforms/44x/warp.c +++ b/arch/powerpc/platforms/44x/warp.c @@ -137,7 +137,7 @@ static int __init pika_dtm_start(void) } of_node_put(np); - fpga = ioremap(res.start + 0x20, 4); + fpga = ioremap(res.start, 0x24); if (fpga == NULL) return -ENOENT; diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig new file mode 100644 index 00000000000..c6fa49e23dc --- /dev/null +++ b/arch/powerpc/platforms/512x/Kconfig @@ -0,0 +1,20 @@ +config PPC_MPC512x + bool + select FSL_SOC + select IPIC + default n + +config PPC_MPC5121 + bool + select PPC_MPC512x + default n + +config MPC5121_ADS + bool "Freescale MPC5121E ADS" + depends on PPC_MULTIPLATFORM && PPC32 + select DEFAULT_UIMAGE + select WANT_DEVICE_TREE + select PPC_MPC5121 + help + This option enables support for the MPC5121E ADS board. + default n diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile new file mode 100644 index 00000000000..232c89f2039 --- /dev/null +++ b/arch/powerpc/platforms/512x/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the Freescale PowerPC 512x linux kernel. +# +obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/platforms/512x/mpc5121_ads.c new file mode 100644 index 00000000000..50bd3a31902 --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc5121_ads.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com>, Thur Mar 29 2007 + * + * Description: + * MPC5121 ADS board setup + * + * This 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 <linux/kernel.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/prom.h> +#include <asm/time.h> + +/** + * mpc512x_find_ips_freq - Find the IPS bus frequency for a device + * @node: device node + * + * Returns IPS bus frequency, or 0 if the bus frequency cannot be found. + */ +unsigned long +mpc512x_find_ips_freq(struct device_node *node) +{ + struct device_node *np; + const unsigned int *p_ips_freq = NULL; + + of_node_get(node); + while (node) { + p_ips_freq = of_get_property(node, "bus-frequency", NULL); + if (p_ips_freq) + break; + + np = of_get_parent(node); + of_node_put(node); + node = np; + } + if (node) + of_node_put(node); + + return p_ips_freq ? *p_ips_freq : 0; +} +EXPORT_SYMBOL(mpc512x_find_ips_freq); + +static struct of_device_id __initdata of_bus_ids[] = { + { .name = "soc", }, + { .name = "localbus", }, + {}, +}; + +static void __init mpc5121_ads_declare_of_platform_devices(void) +{ + /* Find every child of the SOC node and add it to of_platform */ + if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) + printk(KERN_ERR __FILE__ ": " + "Error while probing of_platform bus\n"); +} + +static void __init mpc5121_ads_init_IRQ(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,ipic"); + if (!np) + return; + + ipic_init(np, 0); + of_node_put(np); + + /* + * Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + ipic_set_default_priority(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc5121_ads_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc5121ads"); +} + +define_machine(mpc5121_ads) { + .name = "MPC5121 ADS", + .probe = mpc5121_ads_probe, + .init = mpc5121_ads_declare_of_platform_devices, + .init_IRQ = mpc5121_ads_init_IRQ, + .get_irq = ipic_get_irq, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/82xx/mpc8272_ads.c b/arch/powerpc/platforms/82xx/mpc8272_ads.c index 3fce6b375db..7d301875198 100644 --- a/arch/powerpc/platforms/82xx/mpc8272_ads.c +++ b/arch/powerpc/platforms/82xx/mpc8272_ads.c @@ -134,13 +134,12 @@ static void __init mpc8272_ads_setup_arch(void) } bcsr = of_iomap(np, 0); + of_node_put(np); if (!bcsr) { printk(KERN_ERR "Cannot map BCSR registers\n"); return; } - of_node_put(np); - clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); setbits32(&bcsr[1], BCSR1_FETH_RST); diff --git a/arch/powerpc/platforms/82xx/pq2fads.c b/arch/powerpc/platforms/82xx/pq2fads.c index 68196e34999..e1dceeec499 100644 --- a/arch/powerpc/platforms/82xx/pq2fads.c +++ b/arch/powerpc/platforms/82xx/pq2fads.c @@ -130,13 +130,12 @@ static void __init pq2fads_setup_arch(void) } bcsr = of_iomap(np, 0); + of_node_put(np); if (!bcsr) { printk(KERN_ERR "Cannot map BCSR registers\n"); return; } - of_node_put(np); - /* Enable the serial and ethernet ports */ clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 9f0fd88b2b1..e7f706b624f 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -101,7 +101,7 @@ static void __init mpc832x_rdb_setup_arch(void) #ifdef CONFIG_QUICC_ENGINE qe_reset(); - if ((np = of_find_node_by_name(np, "par_io")) != NULL) { + if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) { par_io_init(np); of_node_put(np); diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 88bb748aff0..68065e62fc3 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -14,6 +14,8 @@ #define MPC83XX_SCCR_USB_DRCM_11 0x00300000 #define MPC83XX_SCCR_USB_DRCM_01 0x00100000 #define MPC83XX_SCCR_USB_DRCM_10 0x00200000 +#define MPC8315_SCCR_USB_MASK 0x00c00000 +#define MPC8315_SCCR_USB_DRCM_11 0x00c00000 #define MPC837X_SCCR_USB_DRCM_11 0x00c00000 /* system i/o configuration register low */ diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c index 681230a30ac..471fdd8f410 100644 --- a/arch/powerpc/platforms/83xx/usb.c +++ b/arch/powerpc/platforms/83xx/usb.c @@ -104,6 +104,7 @@ int mpc831x_usb_cfg(void) u32 temp; void __iomem *immap, *usb_regs; struct device_node *np = NULL; + struct device_node *immr_node = NULL; const void *prop; struct resource res; int ret = 0; @@ -124,10 +125,15 @@ int mpc831x_usb_cfg(void) } /* Configure clock */ - temp = in_be32(immap + MPC83XX_SCCR_OFFS); - temp &= ~MPC83XX_SCCR_USB_MASK; - temp |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ - out_be32(immap + MPC83XX_SCCR_OFFS, temp); + immr_node = of_get_parent(np); + if (immr_node && of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC8315_SCCR_USB_MASK, + MPC8315_SCCR_USB_DRCM_11); + else + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); /* Configure pin mux for ULPI. There is no pin mux for UTMI */ if (prop && !strcmp(prop, "ulpi")) { @@ -144,6 +150,9 @@ int mpc831x_usb_cfg(void) iounmap(immap); + if (immr_node) + of_node_put(immr_node); + /* Map USB SOC space */ ret = of_address_to_resource(np, 0, &res); if (ret) { diff --git a/arch/powerpc/platforms/8xx/adder875.c b/arch/powerpc/platforms/8xx/adder875.c index c6bc0783c3b..82363e98f50 100644 --- a/arch/powerpc/platforms/8xx/adder875.c +++ b/arch/powerpc/platforms/8xx/adder875.c @@ -15,12 +15,12 @@ #include <asm/time.h> #include <asm/machdep.h> -#include <asm/commproc.h> +#include <asm/cpm1.h> #include <asm/fs_pd.h> #include <asm/udbg.h> #include <asm/prom.h> -#include <sysdev/commproc.h> +#include "mpc8xx.h" struct cpm_pin { int port, pin, flags; @@ -108,7 +108,7 @@ define_machine(adder875) { .name = "Adder MPC875", .probe = adder875_probe, .setup_arch = adder875_setup, - .init_IRQ = m8xx_pic_init, + .init_IRQ = mpc8xx_pics_init, .get_irq = mpc8xx_get_irq, .restart = mpc8xx_restart, .calibrate_decr = generic_calibrate_decr, diff --git a/arch/powerpc/platforms/8xx/ep88xc.c b/arch/powerpc/platforms/8xx/ep88xc.c index a8dffa00577..7d9ac6040d6 100644 --- a/arch/powerpc/platforms/8xx/ep88xc.c +++ b/arch/powerpc/platforms/8xx/ep88xc.c @@ -15,7 +15,6 @@ #include <asm/machdep.h> #include <asm/io.h> #include <asm/udbg.h> -#include <asm/commproc.h> #include <asm/cpm1.h> #include "mpc8xx.h" diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index fdce10c4f07..fcedbec07f9 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -24,6 +24,7 @@ config PPC_83xx select MPC83xx select IPIC select WANT_DEVICE_TREE + select FSL_EMB_PERFMON config PPC_86xx bool "Freescale 86xx" @@ -41,6 +42,7 @@ config CLASSIC32 source "arch/powerpc/platforms/pseries/Kconfig" source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" +source "arch/powerpc/platforms/512x/Kconfig" source "arch/powerpc/platforms/52xx/Kconfig" source "arch/powerpc/platforms/powermac/Kconfig" source "arch/powerpc/platforms/prep/Kconfig" diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 7fc41104d53..69941ba7097 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -14,7 +14,7 @@ choice There are five families of 32 bit PowerPC chips supported. The most common ones are the desktop and server CPUs (601, 603, 604, 740, 750, 74xx) CPUs from Freescale and IBM, with their - embedded 52xx/82xx/83xx/86xx counterparts. + embedded 512x/52xx/82xx/83xx/86xx counterparts. The other embeeded parts, namely 4xx, 8xx, e200 (55xx) and e500 (85xx) each form a family of their own that is not compatible with the others. @@ -22,7 +22,7 @@ choice If unsure, select 52xx/6xx/7xx/74xx/82xx/83xx/86xx. config 6xx - bool "52xx/6xx/7xx/74xx/82xx/83xx/86xx" + bool "512x/52xx/6xx/7xx/74xx/82xx/83xx/86xx" select PPC_FPU config PPC_85xx @@ -94,6 +94,7 @@ config 8xx bool config E500 + select FSL_EMB_PERFMON bool config PPC_FPU @@ -115,6 +116,9 @@ config FSL_BOOKE depends on E200 || E500 default y +config FSL_EMB_PERFMON + bool + config PTE_64BIT bool depends on 44x || E500 @@ -221,7 +225,7 @@ config NR_CPUS config NOT_COHERENT_CACHE bool - depends on 4xx || 8xx || E200 + depends on 4xx || 8xx || E200 || PPC_MPC512x default y config CHECK_CACHE_COHERENCY diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 6d9079da5f5..a984894466d 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -11,6 +11,7 @@ endif obj-$(CONFIG_PPC_CHRP) += chrp/ obj-$(CONFIG_40x) += 40x/ obj-$(CONFIG_44x) += 44x/ +obj-$(CONFIG_PPC_MPC512x) += 512x/ obj-$(CONFIG_PPC_MPC52xx) += 52xx/ obj-$(CONFIG_PPC_8xx) += 8xx/ obj-$(CONFIG_PPC_82xx) += 82xx/ diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 3a963b4a9be..2f169991896 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -54,6 +54,13 @@ config SPU_FS_64K_LS uses 4K pages. This can improve performances of applications using multiple SPEs by lowering the TLB pressure on them. +config SPU_TRACE + tristate "SPU event tracing support" + depends on SPU_FS && MARKERS + help + This option allows reading a trace of spu-related events through + the sputrace file in procfs. + config SPU_BASE bool default n diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c index 095988f13bf..d95e71dee91 100644 --- a/arch/powerpc/platforms/cell/axon_msi.c +++ b/arch/powerpc/platforms/cell/axon_msi.c @@ -13,7 +13,7 @@ #include <linux/kernel.h> #include <linux/pci.h> #include <linux/msi.h> -#include <linux/reboot.h> +#include <linux/of_platform.h> #include <asm/dcr.h> #include <asm/machdep.h> @@ -65,14 +65,12 @@ struct axon_msic { struct irq_host *irq_host; - __le32 *fifo; + __le32 *fifo_virt; + dma_addr_t fifo_phys; dcr_host_t dcr_host; - struct list_head list; u32 read_offset; }; -static LIST_HEAD(axon_msic_list); - static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val) { pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n); @@ -94,7 +92,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc) while (msic->read_offset != write_offset) { idx = msic->read_offset / sizeof(__le32); - msi = le32_to_cpu(msic->fifo[idx]); + msi = le32_to_cpu(msic->fifo_virt[idx]); msi &= 0xFFFF; pr_debug("axon_msi: woff %x roff %x msi %x\n", @@ -139,6 +137,7 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev) tmp = dn; dn = of_find_node_by_phandle(*ph); + of_node_put(tmp); if (!dn) { dev_dbg(&dev->dev, "axon_msi: msi-translator doesn't point to a node\n"); @@ -156,7 +155,6 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev) out_error: of_node_put(dn); - of_node_put(tmp); return msic; } @@ -292,30 +290,24 @@ static struct irq_host_ops msic_host_ops = { .map = msic_host_map, }; -static int axon_msi_notify_reboot(struct notifier_block *nb, - unsigned long code, void *data) +static int axon_msi_shutdown(struct of_device *device) { - struct axon_msic *msic; + struct axon_msic *msic = device->dev.platform_data; u32 tmp; - list_for_each_entry(msic, &axon_msic_list, list) { - pr_debug("axon_msi: disabling %s\n", - msic->irq_host->of_node->full_name); - tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); - tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; - msic_dcr_write(msic, MSIC_CTRL_REG, tmp); - } + pr_debug("axon_msi: disabling %s\n", + msic->irq_host->of_node->full_name); + tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); + tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; + msic_dcr_write(msic, MSIC_CTRL_REG, tmp); return 0; } -static struct notifier_block axon_msi_reboot_notifier = { - .notifier_call = axon_msi_notify_reboot -}; - -static int axon_msi_setup_one(struct device_node *dn) +static int axon_msi_probe(struct of_device *device, + const struct of_device_id *device_id) { - struct page *page; + struct device_node *dn = device->node; struct axon_msic *msic; unsigned int virq; int dcr_base, dcr_len; @@ -346,16 +338,14 @@ static int axon_msi_setup_one(struct device_node *dn) goto out_free_msic; } - page = alloc_pages_node(of_node_to_nid(dn), GFP_KERNEL, - get_order(MSIC_FIFO_SIZE_BYTES)); - if (!page) { + msic->fifo_virt = dma_alloc_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, + &msic->fifo_phys, GFP_KERNEL); + if (!msic->fifo_virt) { printk(KERN_ERR "axon_msi: couldn't allocate fifo for %s\n", dn->full_name); goto out_free_msic; } - msic->fifo = page_address(page); - msic->irq_host = irq_alloc_host(of_node_get(dn), IRQ_HOST_MAP_NOMAP, NR_IRQS, &msic_host_ops, 0); if (!msic->irq_host) { @@ -378,14 +368,18 @@ static int axon_msi_setup_one(struct device_node *dn) pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq); /* Enable the MSIC hardware */ - msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, (u64)msic->fifo >> 32); + msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32); msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG, - (u64)msic->fifo & 0xFFFFFFFF); + msic->fifo_phys & 0xFFFFFFFF); msic_dcr_write(msic, MSIC_CTRL_REG, MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE | MSIC_CTRL_FIFO_SIZE); - list_add(&msic->list, &axon_msic_list); + device->dev.platform_data = msic; + + ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs; + ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs; + ppc_md.msi_check_device = axon_msi_check_device; printk(KERN_DEBUG "axon_msi: setup MSIC on %s\n", dn->full_name); @@ -394,7 +388,8 @@ static int axon_msi_setup_one(struct device_node *dn) out_free_host: kfree(msic->irq_host); out_free_fifo: - __free_pages(virt_to_page(msic->fifo), get_order(MSIC_FIFO_SIZE_BYTES)); + dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt, + msic->fifo_phys); out_free_msic: kfree(msic); out: @@ -402,28 +397,24 @@ out: return -1; } -static int axon_msi_init(void) -{ - struct device_node *dn; - int found = 0; - - pr_debug("axon_msi: initialising ...\n"); - - for_each_compatible_node(dn, NULL, "ibm,axon-msic") { - if (axon_msi_setup_one(dn) == 0) - found++; - } - - if (found) { - ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs; - ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs; - ppc_md.msi_check_device = axon_msi_check_device; - - register_reboot_notifier(&axon_msi_reboot_notifier); +static const struct of_device_id axon_msi_device_id[] = { + { + .compatible = "ibm,axon-msic" + }, + {} +}; - pr_debug("axon_msi: registered callbacks!\n"); - } +static struct of_platform_driver axon_msi_driver = { + .match_table = axon_msi_device_id, + .probe = axon_msi_probe, + .shutdown = axon_msi_shutdown, + .driver = { + .name = "axon-msi" + }, +}; - return 0; +static int __init axon_msi_init(void) +{ + return of_register_platform_driver(&axon_msi_driver); } -arch_initcall(axon_msi_init); +subsys_initcall(axon_msi_init); diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index df330666ccc..edab631a8dc 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -26,6 +26,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/notifier.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <asm/prom.h> @@ -789,18 +790,16 @@ static int __init cell_iommu_init_disabled(void) static u64 cell_iommu_get_fixed_address(struct device *dev) { u64 cpu_addr, size, best_size, pci_addr = OF_BAD_ADDR; - struct device_node *tmp, *np; + struct device_node *np; const u32 *ranges = NULL; int i, len, best; - np = dev->archdata.of_node; - of_node_get(np); - ranges = of_get_property(np, "dma-ranges", &len); - while (!ranges && np) { - tmp = of_get_parent(np); - of_node_put(np); - np = tmp; + np = of_node_get(dev->archdata.of_node); + while (np) { ranges = of_get_property(np, "dma-ranges", &len); + if (ranges) + break; + np = of_get_next_parent(np); } if (!ranges) { @@ -842,19 +841,18 @@ static int dma_set_mask_and_switch(struct device *dev, u64 dma_mask) if (!dev->dma_mask || !dma_supported(dev, dma_mask)) return -EIO; - if (dma_mask == DMA_BIT_MASK(64)) { - if (cell_iommu_get_fixed_address(dev) == OF_BAD_ADDR) - dev_dbg(dev, "iommu: 64-bit OK, but bad addr\n"); - else { - dev_dbg(dev, "iommu: 64-bit OK, using fixed ops\n"); - set_dma_ops(dev, &dma_iommu_fixed_ops); - cell_dma_dev_setup(dev); - } + if (dma_mask == DMA_BIT_MASK(64) && + cell_iommu_get_fixed_address(dev) != OF_BAD_ADDR) + { + dev_dbg(dev, "iommu: 64-bit OK, using fixed ops\n"); + set_dma_ops(dev, &dma_iommu_fixed_ops); } else { dev_dbg(dev, "iommu: not 64-bit, using default ops\n"); set_dma_ops(dev, get_pci_dma_ops()); } + cell_dma_dev_setup(dev); + *dev->dma_mask = dma_mask; return 0; @@ -918,6 +916,18 @@ static int __init cell_iommu_fixed_mapping_init(void) return -1; } + /* We must have dma-ranges properties for fixed mapping to work */ + for (np = NULL; (np = of_find_all_nodes(np));) { + if (of_find_property(np, "dma-ranges", NULL)) + break; + } + of_node_put(np); + + if (!np) { + pr_debug("iommu: no dma-ranges found, no fixed mapping\n"); + return -1; + } + /* The default setup is to have the fixed mapping sit after the * dynamic region, so find the top of the largest IOMMU window * on any axon, then add the size of RAM and that's our max value. @@ -981,8 +991,8 @@ static int __init cell_iommu_fixed_mapping_init(void) dsize = htab_size_bytes; } - pr_debug("iommu: setting up %d, dynamic window %lx-%lx " \ - "fixed window %lx-%lx\n", iommu->nid, dbase, + printk(KERN_DEBUG "iommu: node %d, dynamic window 0x%lx-0x%lx " + "fixed window 0x%lx-0x%lx\n", iommu->nid, dbase, dbase + dsize, fbase, fbase + fsize); cell_iommu_setup_page_tables(iommu, dbase, dsize, fbase, fsize); @@ -998,8 +1008,6 @@ static int __init cell_iommu_fixed_mapping_init(void) dma_iommu_ops.set_dma_mask = dma_set_mask_and_switch; set_pci_dma_ops(&dma_iommu_ops); - printk(KERN_DEBUG "IOMMU fixed mapping established.\n"); - return 0; } diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index e6534b519c9..a7f609b3b87 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -98,7 +98,7 @@ static int __init cell_publish_devices(void) } return 0; } -machine_device_initcall(cell, cell_publish_devices); +machine_subsys_initcall(cell, cell_publish_devices); static void cell_mpic_cascade(unsigned int irq, struct irq_desc *desc) { diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile index d3a349fb42e..99610a6361f 100644 --- a/arch/powerpc/platforms/cell/spufs/Makefile +++ b/arch/powerpc/platforms/cell/spufs/Makefile @@ -4,6 +4,8 @@ spufs-y += inode.o file.o context.o syscalls.o coredump.o spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o spufs-y += switch.o fault.o lscsa_alloc.o +obj-$(CONFIG_SPU_TRACE) += sputrace.o + # Rules to build switch.o with the help of SPU tool chain SPU_CROSS := spu- SPU_CC := $(SPU_CROSS)gcc diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index 50d98a154aa..64eb15b2204 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -288,6 +288,12 @@ static void spu_backing_runcntl_write(struct spu_context *ctx, u32 val) spin_lock(&ctx->csa.register_lock); ctx->csa.prob.spu_runcntl_RW = val; if (val & SPU_RUNCNTL_RUNNABLE) { + ctx->csa.prob.spu_status_R &= + ~SPU_STATUS_STOPPED_BY_STOP & + ~SPU_STATUS_STOPPED_BY_HALT & + ~SPU_STATUS_SINGLE_STEP & + ~SPU_STATUS_INVALID_INSTR & + ~SPU_STATUS_INVALID_CH; ctx->csa.prob.spu_status_R |= SPU_STATUS_RUNNING; } else { ctx->csa.prob.spu_status_R &= ~SPU_STATUS_RUNNING; diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index eff4d291ba8..e46d300e21a 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -108,7 +108,7 @@ int spufs_handle_class1(struct spu_context *ctx) u64 ea, dsisr, access; unsigned long flags; unsigned flt = 0; - int ret, ret2; + int ret; /* * dar and dsisr get passed from the registers @@ -148,13 +148,10 @@ int spufs_handle_class1(struct spu_context *ctx) ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt); /* - * If spu_acquire fails due to a pending signal we just want to return - * EINTR to userspace even if that means missing the dma restart or - * updating the page fault statistics. + * This is nasty: we need the state_mutex for all the bookkeeping even + * if the syscall was interrupted by a signal. ewww. */ - ret2 = spu_acquire(ctx); - if (ret2) - goto out; + mutex_lock(&ctx->state_mutex); /* * Clear dsisr under ctxt lock after handling the fault, so that @@ -185,7 +182,6 @@ int spufs_handle_class1(struct spu_context *ctx) } else spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE); - out: spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); return ret; } diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 3fcd06418b0..c66c3756970 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -29,6 +29,7 @@ #include <linux/poll.h> #include <linux/ptrace.h> #include <linux/seq_file.h> +#include <linux/marker.h> #include <asm/io.h> #include <asm/semaphore.h> @@ -357,6 +358,9 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, { struct spu_context *ctx = vma->vm_file->private_data; unsigned long area, offset = address - vma->vm_start; + int ret = 0; + + spu_context_nospu_trace(spufs_ps_nopfn__enter, ctx); offset += vma->vm_pgoff << PAGE_SHIFT; if (offset >= ps_size) @@ -375,14 +379,18 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, if (ctx->state == SPU_STATE_SAVED) { up_read(¤t->mm->mmap_sem); - spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); + spu_context_nospu_trace(spufs_ps_nopfn__sleep, ctx); + ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); + spu_context_trace(spufs_ps_nopfn__wake, ctx, ctx->spu); down_read(¤t->mm->mmap_sem); } else { area = ctx->spu->problem_phys + ps_offs; vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); + spu_context_trace(spufs_ps_nopfn__insert, ctx, ctx->spu); } - spu_release(ctx); + if (!ret) + spu_release(ctx); return NOPFN_REFAULT; } @@ -454,7 +462,7 @@ static int spufs_cntl_open(struct inode *inode, struct file *file) if (!i->i_openers++) ctx->cntl = inode->i_mapping; mutex_unlock(&ctx->mapping_lock); - return spufs_attr_open(inode, file, spufs_cntl_get, + return simple_attr_open(inode, file, spufs_cntl_get, spufs_cntl_set, "0x%08lx"); } @@ -464,7 +472,7 @@ spufs_cntl_release(struct inode *inode, struct file *file) struct spufs_inode_info *i = SPUFS_I(inode); struct spu_context *ctx = i->i_ctx; - spufs_attr_release(inode, file); + simple_attr_release(inode, file); mutex_lock(&ctx->mapping_lock); if (!--i->i_openers) @@ -476,8 +484,8 @@ spufs_cntl_release(struct inode *inode, struct file *file) static const struct file_operations spufs_cntl_fops = { .open = spufs_cntl_open, .release = spufs_cntl_release, - .read = spufs_attr_read, - .write = spufs_attr_write, + .read = simple_attr_read, + .write = simple_attr_write, .mmap = spufs_cntl_mmap, }; @@ -749,23 +757,25 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf, count = spu_acquire(ctx); if (count) - return count; + goto out; /* wait only for the first element */ count = 0; if (file->f_flags & O_NONBLOCK) { - if (!spu_ibox_read(ctx, &ibox_data)) + if (!spu_ibox_read(ctx, &ibox_data)) { count = -EAGAIN; + goto out_unlock; + } } else { count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); + if (count) + goto out; } - if (count) - goto out; /* if we can't write at all, return -EFAULT */ count = __put_user(ibox_data, udata); if (count) - goto out; + goto out_unlock; for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { int ret; @@ -782,9 +792,9 @@ static ssize_t spufs_ibox_read(struct file *file, char __user *buf, break; } -out: +out_unlock: spu_release(ctx); - +out: return count; } @@ -899,7 +909,7 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, count = spu_acquire(ctx); if (count) - return count; + goto out; /* * make sure we can at least write one element, by waiting @@ -907,14 +917,16 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, */ count = 0; if (file->f_flags & O_NONBLOCK) { - if (!spu_wbox_write(ctx, wbox_data)) + if (!spu_wbox_write(ctx, wbox_data)) { count = -EAGAIN; + goto out_unlock; + } } else { count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); + if (count) + goto out; } - if (count) - goto out; /* write as much as possible */ for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { @@ -928,8 +940,9 @@ static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, break; } -out: +out_unlock: spu_release(ctx); +out: return count; } @@ -1592,12 +1605,11 @@ static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, } else { ret = spufs_wait(ctx->mfc_wq, spufs_read_mfc_tagstatus(ctx, &status)); + if (ret) + goto out; } spu_release(ctx); - if (ret) - goto out; - ret = 4; if (copy_to_user(buffer, &status, 4)) ret = -EFAULT; @@ -1726,6 +1738,8 @@ static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, int status; ret = spufs_wait(ctx->mfc_wq, spu_send_mfc_command(ctx, cmd, &status)); + if (ret) + goto out; if (status) ret = status; } @@ -1779,7 +1793,7 @@ static int spufs_mfc_flush(struct file *file, fl_owner_t id) ret = spu_acquire(ctx); if (ret) - return ret; + goto out; #if 0 /* this currently hangs */ ret = spufs_wait(ctx->mfc_wq, @@ -1788,12 +1802,13 @@ static int spufs_mfc_flush(struct file *file, fl_owner_t id) goto out; ret = spufs_wait(ctx->mfc_wq, ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); -out: + if (ret) + goto out; #else ret = 0; #endif spu_release(ctx); - +out: return ret; } diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index c0e968a4c21..e6e6559c55e 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -322,7 +322,7 @@ static struct spu_context * spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, struct file *filp) { - struct spu_context *tmp, *neighbor; + struct spu_context *tmp, *neighbor, *err; int count, node; int aff_supp; @@ -354,11 +354,15 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) && !list_is_last(&neighbor->aff_list, &gang->aff_list_head) && !list_entry(neighbor->aff_list.next, struct spu_context, - aff_list)->aff_head) - return ERR_PTR(-EEXIST); + aff_list)->aff_head) { + err = ERR_PTR(-EEXIST); + goto out_put_neighbor; + } - if (gang != neighbor->gang) - return ERR_PTR(-EINVAL); + if (gang != neighbor->gang) { + err = ERR_PTR(-EINVAL); + goto out_put_neighbor; + } count = 1; list_for_each_entry(tmp, &gang->aff_list_head, aff_list) @@ -372,11 +376,17 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, break; } - if (node == MAX_NUMNODES) - return ERR_PTR(-EEXIST); + if (node == MAX_NUMNODES) { + err = ERR_PTR(-EEXIST); + goto out_put_neighbor; + } } return neighbor; + +out_put_neighbor: + put_spu_context(neighbor); + return err; } static void @@ -454,9 +464,12 @@ spufs_create_context(struct inode *inode, struct dentry *dentry, if (ret) goto out_aff_unlock; - if (affinity) + if (affinity) { spufs_set_affinity(flags, SPUFS_I(dentry->d_inode)->i_ctx, neighbor); + if (neighbor) + put_spu_context(neighbor); + } /* * get references for dget and mntget, will be released @@ -742,8 +755,11 @@ spufs_fill_super(struct super_block *sb, void *data, int silent) .statfs = simple_statfs, .delete_inode = spufs_delete_inode, .drop_inode = generic_delete_inode, + .show_options = generic_show_options, }; + save_mount_options(sb, data); + sb->s_maxbytes = MAX_LFS_FILESIZE; sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index c01a09da1e5..fca22e18069 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -53,7 +53,7 @@ int spu_stopped(struct spu_context *ctx, u32 *stat) stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP | SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP; - if (*stat & stopped) + if (!(*stat & SPU_STATUS_RUNNING) && (*stat & stopped)) return 1; dsisr = ctx->csa.dsisr; @@ -354,8 +354,15 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) do { ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status)); - if (unlikely(ret)) + if (unlikely(ret)) { + /* + * This is nasty: we need the state_mutex for all the + * bookkeeping even if the syscall was interrupted by + * a signal. ewww. + */ + mutex_lock(&ctx->state_mutex); break; + } spu = ctx->spu; if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))) { @@ -388,16 +395,14 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_SINGLE_STEP))); - if ((status & SPU_STATUS_STOPPED_BY_STOP) && - (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100) && - (ctx->state == SPU_STATE_RUNNABLE)) - ctx->stats.libassist++; - - spu_disable_spu(ctx); ret = spu_run_fini(ctx, npc, &status); spu_yield(ctx); + if ((status & SPU_STATUS_STOPPED_BY_STOP) && + (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) + ctx->stats.libassist++; + if ((ret == 0) || ((ret == -ERESTARTSYS) && ((status & SPU_STATUS_STOPPED_BY_HALT) || @@ -410,8 +415,11 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) * since we have TIF_SINGLESTEP set, thus the kernel will do * it upon return from the syscall anyawy */ - if ((status & SPU_STATUS_STOPPED_BY_STOP) - && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) { + if (unlikely(status & SPU_STATUS_SINGLE_STEP)) + ret = -ERESTARTSYS; + + else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP) + && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) { force_sig(SIGTRAP, current); ret = -ERESTARTSYS; } diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 00d914232af..5915343e259 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -39,6 +39,7 @@ #include <linux/pid_namespace.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/marker.h> #include <asm/io.h> #include <asm/mmu_context.h> @@ -216,8 +217,8 @@ void do_notify_spus_active(void) */ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) { - pr_debug("%s: pid=%d SPU=%d NODE=%d\n", __FUNCTION__, current->pid, - spu->number, spu->node); + spu_context_trace(spu_bind_context__enter, ctx, spu); + spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); if (ctx->flags & SPU_CREATE_NOSCHED) @@ -399,8 +400,8 @@ static int has_affinity(struct spu_context *ctx) */ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) { - pr_debug("%s: unbind pid=%d SPU=%d NODE=%d\n", __FUNCTION__, - spu->pid, spu->number, spu->node); + spu_context_trace(spu_unbind_context__enter, ctx, spu); + spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); if (spu->ctx->flags & SPU_CREATE_NOSCHED) @@ -528,6 +529,8 @@ static struct spu *spu_get_idle(struct spu_context *ctx) struct spu *spu, *aff_ref_spu; int node, n; + spu_context_nospu_trace(spu_get_idle__enter, ctx); + if (ctx->gang) { mutex_lock(&ctx->gang->aff_mutex); if (has_affinity(ctx)) { @@ -546,8 +549,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx) if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) ctx->gang->aff_ref_spu = NULL; mutex_unlock(&ctx->gang->aff_mutex); - - return NULL; + goto not_found; } mutex_unlock(&ctx->gang->aff_mutex); } @@ -565,12 +567,14 @@ static struct spu *spu_get_idle(struct spu_context *ctx) mutex_unlock(&cbe_spu_info[node].list_mutex); } + not_found: + spu_context_nospu_trace(spu_get_idle__not_found, ctx); return NULL; found: spu->alloc_state = SPU_USED; mutex_unlock(&cbe_spu_info[node].list_mutex); - pr_debug("Got SPU %d %d\n", spu->number, spu->node); + spu_context_trace(spu_get_idle__found, ctx, spu); spu_init_channels(spu); return spu; } @@ -587,6 +591,8 @@ static struct spu *find_victim(struct spu_context *ctx) struct spu *spu; int node, n; + spu_context_nospu_trace(spu_find_vitim__enter, ctx); + /* * Look for a possible preemption candidate on the local node first. * If there is no candidate look at the other nodes. This isn't @@ -640,6 +646,8 @@ static struct spu *find_victim(struct spu_context *ctx) goto restart; } + spu_context_trace(__spu_deactivate__unload, ctx, spu); + mutex_lock(&cbe_spu_info[node].list_mutex); cbe_spu_info[node].nr_active--; spu_unbind_context(spu, victim); @@ -822,6 +830,7 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) */ void spu_deactivate(struct spu_context *ctx) { + spu_context_nospu_trace(spu_deactivate__enter, ctx); __spu_deactivate(ctx, 1, MAX_PRIO); } @@ -835,6 +844,7 @@ void spu_deactivate(struct spu_context *ctx) */ void spu_yield(struct spu_context *ctx) { + spu_context_nospu_trace(spu_yield__enter, ctx); if (!(ctx->flags & SPU_CREATE_NOSCHED)) { mutex_lock(&ctx->state_mutex); __spu_deactivate(ctx, 0, MAX_PRIO); @@ -864,11 +874,15 @@ static noinline void spusched_tick(struct spu_context *ctx) goto out; spu = ctx->spu; + + spu_context_trace(spusched_tick__preempt, ctx, spu); + new = grab_runnable_context(ctx->prio + 1, spu->node); if (new) { spu_unschedule(spu, ctx); spu_add_to_rq(ctx); } else { + spu_context_nospu_trace(spusched_tick__newslice, ctx); ctx->time_slice++; } out: diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 0e114038ea6..2c2fe3c07d7 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -268,6 +268,9 @@ extern char *isolated_loader; * Same as wait_event_interruptible(), except that here * we need to call spu_release(ctx) before sleeping, and * then spu_acquire(ctx) when awoken. + * + * Returns with state_mutex re-acquired when successfull or + * with -ERESTARTSYS and the state_mutex dropped when interrupted. */ #define spufs_wait(wq, condition) \ @@ -278,11 +281,11 @@ extern char *isolated_loader; prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE); \ if (condition) \ break; \ + spu_release(ctx); \ if (signal_pending(current)) { \ __ret = -ERESTARTSYS; \ break; \ } \ - spu_release(ctx); \ schedule(); \ __ret = spu_acquire(ctx); \ if (__ret) \ @@ -325,4 +328,9 @@ extern void spu_free_lscsa(struct spu_state *csa); extern void spuctx_switch_state(struct spu_context *ctx, enum spu_utilization_state new_state); +#define spu_context_trace(name, ctx, spu) \ + trace_mark(name, "%p %p", ctx, spu); +#define spu_context_nospu_trace(name, ctx) \ + trace_mark(name, "%p", ctx); + #endif diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c new file mode 100644 index 00000000000..2b1953f6f12 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/sputrace.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH + * Released under GPL v2. + * + * Partially based on net/ipv4/tcp_probe.c. + * + * Simple tracing facility for spu contexts. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/marker.h> +#include <linux/proc_fs.h> +#include <linux/wait.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> +#include "spufs.h" + +struct spu_probe { + const char *name; + const char *format; + marker_probe_func *probe_func; +}; + +struct sputrace { + ktime_t tstamp; + int owner_tid; /* owner */ + int curr_tid; + const char *name; + int number; +}; + +static int bufsize __read_mostly = 16384; +MODULE_PARM_DESC(bufsize, "Log buffer size (number of records)"); +module_param(bufsize, int, 0); + + +static DEFINE_SPINLOCK(sputrace_lock); +static DECLARE_WAIT_QUEUE_HEAD(sputrace_wait); +static ktime_t sputrace_start; +static unsigned long sputrace_head, sputrace_tail; +static struct sputrace *sputrace_log; + +static int sputrace_used(void) +{ + return (sputrace_head - sputrace_tail) % bufsize; +} + +static inline int sputrace_avail(void) +{ + return bufsize - sputrace_used(); +} + +static int sputrace_sprint(char *tbuf, int n) +{ + const struct sputrace *t = sputrace_log + sputrace_tail % bufsize; + struct timespec tv = + ktime_to_timespec(ktime_sub(t->tstamp, sputrace_start)); + + return snprintf(tbuf, n, + "[%lu.%09lu] %d: %s (thread = %d, spu = %d)\n", + (unsigned long) tv.tv_sec, + (unsigned long) tv.tv_nsec, + t->owner_tid, + t->name, + t->curr_tid, + t->number); +} + +static ssize_t sputrace_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + int error = 0, cnt = 0; + + if (!buf || len < 0) + return -EINVAL; + + while (cnt < len) { + char tbuf[128]; + int width; + + error = wait_event_interruptible(sputrace_wait, + sputrace_used() > 0); + if (error) + break; + + spin_lock(&sputrace_lock); + if (sputrace_head == sputrace_tail) { + spin_unlock(&sputrace_lock); + continue; + } + + width = sputrace_sprint(tbuf, sizeof(tbuf)); + if (width < len) + sputrace_tail = (sputrace_tail + 1) % bufsize; + spin_unlock(&sputrace_lock); + + if (width >= len) + break; + + error = copy_to_user(buf + cnt, tbuf, width); + if (error) + break; + cnt += width; + } + + return cnt == 0 ? error : cnt; +} + +static int sputrace_open(struct inode *inode, struct file *file) +{ + spin_lock(&sputrace_lock); + sputrace_head = sputrace_tail = 0; + sputrace_start = ktime_get(); + spin_unlock(&sputrace_lock); + + return 0; +} + +static const struct file_operations sputrace_fops = { + .owner = THIS_MODULE, + .open = sputrace_open, + .read = sputrace_read, +}; + +static void sputrace_log_item(const char *name, struct spu_context *ctx, + struct spu *spu) +{ + spin_lock(&sputrace_lock); + if (sputrace_avail() > 1) { + struct sputrace *t = sputrace_log + sputrace_head; + + t->tstamp = ktime_get(); + t->owner_tid = ctx->tid; + t->name = name; + t->curr_tid = current->pid; + t->number = spu ? spu->number : -1; + + sputrace_head = (sputrace_head + 1) % bufsize; + } else { + printk(KERN_WARNING + "sputrace: lost samples due to full buffer.\n"); + } + spin_unlock(&sputrace_lock); + + wake_up(&sputrace_wait); +} + +static void spu_context_event(const struct marker *mdata, + void *private, const char *format, ...) +{ + struct spu_probe *p = mdata->private; + va_list ap; + struct spu_context *ctx; + struct spu *spu; + + va_start(ap, format); + ctx = va_arg(ap, struct spu_context *); + spu = va_arg(ap, struct spu *); + + sputrace_log_item(p->name, ctx, spu); + va_end(ap); +} + +static void spu_context_nospu_event(const struct marker *mdata, + void *private, const char *format, ...) +{ + struct spu_probe *p = mdata->private; + va_list ap; + struct spu_context *ctx; + + va_start(ap, format); + ctx = va_arg(ap, struct spu_context *); + + sputrace_log_item(p->name, ctx, NULL); + va_end(ap); +} + +struct spu_probe spu_probes[] = { + { "spu_bind_context__enter", "%p %p", spu_context_event }, + { "spu_unbind_context__enter", "%p %p", spu_context_event }, + { "spu_get_idle__enter", "%p", spu_context_nospu_event }, + { "spu_get_idle__found", "%p %p", spu_context_event }, + { "spu_get_idle__not_found", "%p", spu_context_nospu_event }, + { "spu_find_victim__enter", "%p", spu_context_nospu_event }, + { "spusched_tick__preempt", "%p %p", spu_context_event }, + { "spusched_tick__newslice", "%p", spu_context_nospu_event }, + { "spu_yield__enter", "%p", spu_context_nospu_event }, + { "spu_deactivate__enter", "%p", spu_context_nospu_event }, + { "__spu_deactivate__unload", "%p %p", spu_context_event }, + { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event }, + { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event }, + { "spufs_ps_nopfn__wake", "%p %p", spu_context_event }, + { "spufs_ps_nopfn__insert", "%p %p", spu_context_event }, + { "spu_acquire_saved__enter", "%p", spu_context_nospu_event }, + { "destroy_spu_context__enter", "%p", spu_context_nospu_event }, +}; + +static int __init sputrace_init(void) +{ + struct proc_dir_entry *entry; + int i, error = -ENOMEM; + + sputrace_log = kcalloc(sizeof(struct sputrace), + bufsize, GFP_KERNEL); + if (!sputrace_log) + goto out; + + entry = create_proc_entry("sputrace", S_IRUSR, NULL); + if (!entry) + goto out_free_log; + entry->proc_fops = &sputrace_fops; + + for (i = 0; i < ARRAY_SIZE(spu_probes); i++) { + struct spu_probe *p = &spu_probes[i]; + + error = marker_probe_register(p->name, p->format, + p->probe_func, p); + if (error) + printk(KERN_INFO "Unable to register probe %s\n", + p->name); + + error = marker_arm(p->name); + if (error) + printk(KERN_INFO "Unable to arm probe %s\n", p->name); + } + + return 0; + +out_free_log: + kfree(sputrace_log); +out: + return -ENOMEM; +} + +static void __exit sputrace_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(spu_probes); i++) + marker_probe_unregister(spu_probes[i].name); + + remove_proc_entry("sputrace", NULL); + kfree(sputrace_log); +} + +module_init(sputrace_init); +module_exit(sputrace_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/powerpc/platforms/embedded6xx/storcenter.c b/arch/powerpc/platforms/embedded6xx/storcenter.c index e12e9d29871..8864e488498 100644 --- a/arch/powerpc/platforms/embedded6xx/storcenter.c +++ b/arch/powerpc/platforms/embedded6xx/storcenter.c @@ -132,33 +132,18 @@ static void __init storcenter_init_IRQ(void) paddr = (phys_addr_t)of_translate_address(dnp, prop); mpic = mpic_alloc(dnp, paddr, MPIC_PRIMARY | MPIC_WANTS_RESET, - 4, 32, " EPIC "); + 16, 32, " OpenPIC "); of_node_put(dnp); BUG_ON(mpic == NULL); - /* PCI IRQs */ /* - * 2.6.12 patch: - * openpic_set_sources(0, 5, OpenPIC_Addr + 0x10200); - * openpic_set_sources(5, 2, OpenPIC_Addr + 0x11120); - * first_irq, num_irqs, __iomem first_ISR - * o_ss: i, src: 0, fdf50200 - * o_ss: i, src: 1, fdf50220 - * o_ss: i, src: 2, fdf50240 - * o_ss: i, src: 3, fdf50260 - * o_ss: i, src: 4, fdf50280 - * o_ss: i, src: 5, fdf51120 - * o_ss: i, src: 6, fdf51140 + * 16 Serial Interrupts followed by 16 Internal Interrupts. + * I2C is the second internal, so it is at 17, 0x11020. */ mpic_assign_isu(mpic, 0, paddr + 0x10200); - mpic_assign_isu(mpic, 1, paddr + 0x10220); - mpic_assign_isu(mpic, 2, paddr + 0x10240); - mpic_assign_isu(mpic, 3, paddr + 0x10260); - mpic_assign_isu(mpic, 4, paddr + 0x10280); - mpic_assign_isu(mpic, 5, paddr + 0x11120); - mpic_assign_isu(mpic, 6, paddr + 0x11140); + mpic_assign_isu(mpic, 1, paddr + 0x11000); mpic_init(mpic); } @@ -178,7 +163,7 @@ static int __init storcenter_probe(void) { unsigned long root = of_get_flat_dt_root(); - return of_flat_dt_is_compatible(root, "storcenter"); + return of_flat_dt_is_compatible(root, "iomega,storcenter"); } define_machine(storcenter){ diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index ba931be2175..5169ecc3712 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -2565,6 +2565,8 @@ static void __init probe_uninorth(void) /* Locate core99 Uni-N */ uninorth_node = of_find_node_by_name(NULL, "uni-n"); + uninorth_maj = 1; + /* Locate G5 u3 */ if (uninorth_node == NULL) { uninorth_node = of_find_node_by_name(NULL, "u3"); @@ -2575,8 +2577,10 @@ static void __init probe_uninorth(void) uninorth_node = of_find_node_by_name(NULL, "u4"); uninorth_maj = 4; } - if (uninorth_node == NULL) + if (uninorth_node == NULL) { + uninorth_maj = 0; return; + } addrp = of_get_property(uninorth_node, "reg", NULL); if (addrp == NULL) @@ -3029,3 +3033,8 @@ void pmac_resume_agp_for_card(struct pci_dev *dev) pmac_agp_resume(pmac_agp_bridge); } EXPORT_SYMBOL(pmac_resume_agp_for_card); + +int pmac_get_uninorth_variant(void) +{ + return uninorth_maj; +} diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index c4ad54e0f28..1f032483c02 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -58,7 +58,7 @@ static void pseries_mach_cpu_die(void) { local_irq_disable(); idle_task_exit(); - xics_teardown_cpu(0); + xics_teardown_cpu(); unregister_slb_shadow(hard_smp_processor_id(), __pa(get_slb_shadow())); rtas_stop_self(); /* Should never get here... */ diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c index 412a5e7aff2..e9dd5fe081c 100644 --- a/arch/powerpc/platforms/pseries/kexec.c +++ b/arch/powerpc/platforms/pseries/kexec.c @@ -54,7 +54,7 @@ void __init setup_kexec_cpu_down_mpic(void) static void pseries_kexec_cpu_down_xics(int crash_shutdown, int secondary) { pseries_kexec_cpu_down(crash_shutdown, secondary); - xics_teardown_cpu(secondary); + xics_kexec_teardown_cpu(secondary); } void __init setup_kexec_cpu_down_xics(void) diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index c02f8742c54..2800fced8c7 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -167,6 +167,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np) if ((child = of_get_next_child(np, NULL))) { of_node_put(child); + of_node_put(parent); return -EBUSY; } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 8f8dd9c3ca6..ca52b587166 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -160,6 +160,46 @@ static inline void lpar_qirr_info(int n_cpu , u8 value) /* High level handlers and init code */ +static void xics_update_irq_servers(void) +{ + int i, j; + struct device_node *np; + u32 ilen; + const u32 *ireg, *isize; + u32 hcpuid; + + /* Find the server numbers for the boot cpu. */ + np = of_get_cpu_node(boot_cpuid, NULL); + BUG_ON(!np); + + ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); + if (!ireg) { + of_node_put(np); + return; + } + + i = ilen / sizeof(int); + hcpuid = get_hard_smp_processor_id(boot_cpuid); + + /* Global interrupt distribution server is specified in the last + * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last + * entry fom this property for current boot cpu id and use it as + * default distribution server + */ + for (j = 0; j < i; j += 2) { + if (ireg[j] == hcpuid) { + default_server = hcpuid; + default_distrib_server = ireg[j+1]; + + isize = of_get_property(np, + "ibm,interrupt-server#-size", NULL); + if (isize) + interrupt_server_size = *isize; + } + } + + of_node_put(np); +} #ifdef CONFIG_SMP static int get_irq_server(unsigned int virq, unsigned int strict_check) @@ -169,6 +209,9 @@ static int get_irq_server(unsigned int virq, unsigned int strict_check) cpumask_t cpumask = irq_desc[virq].affinity; cpumask_t tmp = CPU_MASK_NONE; + if (! cpu_isset(default_server, cpu_online_map)) + xics_update_irq_servers(); + if (!distribute_irqs) return default_server; @@ -658,39 +701,11 @@ static void __init xics_setup_8259_cascade(void) set_irq_chained_handler(cascade, pseries_8259_cascade); } -static struct device_node *cpuid_to_of_node(int cpu) -{ - struct device_node *np; - u32 hcpuid = get_hard_smp_processor_id(cpu); - - for_each_node_by_type(np, "cpu") { - int i, len; - const u32 *intserv; - - intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", - &len); - - if (!intserv) - intserv = of_get_property(np, "reg", &len); - - i = len / sizeof(u32); - - while (i--) - if (intserv[i] == hcpuid) - return np; - } - - return NULL; -} - void __init xics_init_IRQ(void) { - int i, j; struct device_node *np; - u32 ilen, indx = 0; - const u32 *ireg, *isize; + u32 indx = 0; int found = 0; - u32 hcpuid; ppc64_boot_msg(0x20, "XICS Init"); @@ -709,34 +724,7 @@ void __init xics_init_IRQ(void) return; xics_init_host(); - - /* Find the server numbers for the boot cpu. */ - np = cpuid_to_of_node(boot_cpuid); - BUG_ON(!np); - ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); - if (!ireg) - goto skip_gserver_check; - i = ilen / sizeof(int); - hcpuid = get_hard_smp_processor_id(boot_cpuid); - - /* Global interrupt distribution server is specified in the last - * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last - * entry fom this property for current boot cpu id and use it as - * default distribution server - */ - for (j = 0; j < i; j += 2) { - if (ireg[j] == hcpuid) { - default_server = hcpuid; - default_distrib_server = ireg[j+1]; - - isize = of_get_property(np, - "ibm,interrupt-server#-size", NULL); - if (isize) - interrupt_server_size = *isize; - } - } -skip_gserver_check: - of_node_put(np); + xics_update_irq_servers(); if (firmware_has_feature(FW_FEATURE_LPAR)) ppc_md.get_irq = xics_get_irq_lpar; @@ -775,11 +763,9 @@ void xics_request_IPIs(void) } #endif /* CONFIG_SMP */ -void xics_teardown_cpu(int secondary) +void xics_teardown_cpu() { int cpu = smp_processor_id(); - unsigned int ipi; - struct irq_desc *desc; xics_set_cpu_priority(0); @@ -790,9 +776,17 @@ void xics_teardown_cpu(int secondary) lpar_qirr_info(cpu, 0xff); else direct_qirr_info(cpu, 0xff); +} + +void xics_kexec_teardown_cpu(int secondary) +{ + unsigned int ipi; + struct irq_desc *desc; + + xics_teardown_cpu(); /* - * we need to EOI the IPI if we got here from kexec down IPI + * we need to EOI the IPI * * probably need to check all the other interrupts too * should we be flagging idle loop instead? @@ -880,8 +874,8 @@ void xics_migrate_irqs_away(void) virq, cpu); /* Reset affinity to all cpus */ + irq_desc[virq].affinity = CPU_MASK_ALL; desc->chip->set_affinity(virq, CPU_MASK_ALL); - irq_desc[irq].affinity = CPU_MASK_ALL; unlock: spin_unlock_irqrestore(&desc->lock, flags); } diff --git a/arch/powerpc/platforms/pseries/xics.h b/arch/powerpc/platforms/pseries/xics.h index 9ffd809d29e..c26bcff47b6 100644 --- a/arch/powerpc/platforms/pseries/xics.h +++ b/arch/powerpc/platforms/pseries/xics.h @@ -16,7 +16,8 @@ extern void xics_init_IRQ(void); extern void xics_setup_cpu(void); -extern void xics_teardown_cpu(int secondary); +extern void xics_teardown_cpu(void); +extern void xics_kexec_teardown_cpu(int secondary); extern void xics_cause_IPI(int cpu); extern void xics_request_IPIs(void); extern void xics_migrate_irqs_away(void); diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c index 427027c7ea0..437e48d3ae3 100644 --- a/arch/powerpc/sysdev/dcr.c +++ b/arch/powerpc/sysdev/dcr.c @@ -137,5 +137,6 @@ void dcr_unmap(dcr_host_t host, unsigned int dcr_c) h.token = NULL; } EXPORT_SYMBOL_GPL(dcr_unmap); - -#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */ +#else /* defined(CONFIG_PPC_DCR_NATIVE) */ +DEFINE_SPINLOCK(dcr_ind_lock); +#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */ diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index e48b20e934c..2c5388ce902 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -1342,7 +1342,7 @@ static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk, if (ret) goto unreg; - ret = platform_device_register(pdev); + ret = platform_device_add(pdev); if (ret) goto unreg; diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c index 0e74a4bd982..5d2d5522ef4 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.c +++ b/arch/powerpc/sysdev/mpc8xx_pic.c @@ -174,15 +174,19 @@ int mpc8xx_pic_init(void) goto out; siu_reg = ioremap(res.start, res.end - res.start + 1); - if (siu_reg == NULL) - return -EINVAL; + if (siu_reg == NULL) { + ret = -EINVAL; + goto out; + } - mpc8xx_pic_host = irq_alloc_host(of_node_get(np), IRQ_HOST_MAP_LINEAR, + mpc8xx_pic_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 64, &mpc8xx_pic_host_ops, 64); if (mpc8xx_pic_host == NULL) { printk(KERN_ERR "MPC8xx PIC: failed to allocate irq host!\n"); ret = -ENOMEM; + goto out; } + return 0; out: of_node_put(np); diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index 5ef844da935..6efbd5e5bb1 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c @@ -66,7 +66,7 @@ phys_addr_t get_qe_base(void) { struct device_node *qe; unsigned int size; - const void *prop; + const u32 *prop; if (qebase != -1) return qebase; @@ -79,7 +79,8 @@ phys_addr_t get_qe_base(void) } prop = of_get_property(qe, "reg", &size); - qebase = of_translate_address(qe, prop); + if (prop && size >= sizeof(*prop)) + qebase = of_translate_address(qe, prop); of_node_put(qe); return qebase; @@ -172,10 +173,9 @@ unsigned int get_brg_clk(void) } prop = of_get_property(qe, "brg-frequency", &size); - if (!prop || size != sizeof(*prop)) - return brg_clk; + if (prop && size == sizeof(*prop)) + brg_clk = *prop; - brg_clk = *prop; of_node_put(qe); return brg_clk; diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index d51368d72e3..294055902f0 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -275,7 +275,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start =c_start, .next = c_next, .stop = c_stop, diff --git a/arch/ppc/mm/pgtable.c b/arch/ppc/mm/pgtable.c index 409fcaa4994..03a79bff127 100644 --- a/arch/ppc/mm/pgtable.c +++ b/arch/ppc/mm/pgtable.c @@ -95,7 +95,7 @@ __init_refok pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long add return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *ptepage; @@ -106,8 +106,10 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) #endif ptepage = alloc_pages(flags, 0); - if (ptepage) + if (ptepage) { clear_highpage(ptepage); + pgtable_page_ctor(ptepage); + } return ptepage; } @@ -119,11 +121,12 @@ void pte_free_kernel(struct mm_struct *mm, pte_t *pte) free_page((unsigned long)pte); } -void pte_free(struct mm_struct *mm, struct page *ptepage) +void pte_free(struct mm_struct *mm, pgtable_t ptepage) { #ifdef CONFIG_SMP hash_page_sync(); #endif + pgtable_page_dtor(ptepage); __free_page(ptepage); } diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 29ae165d174..f9f8779022a 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -649,21 +649,24 @@ setup_memory(void) /* * Reserve memory used for lowcore/command line/kernel image. */ - reserve_bootmem(0, (unsigned long)_ehead); + reserve_bootmem(0, (unsigned long)_ehead, BOOTMEM_DEFAULT); reserve_bootmem((unsigned long)_stext, - PFN_PHYS(start_pfn) - (unsigned long)_stext); + PFN_PHYS(start_pfn) - (unsigned long)_stext, + BOOTMEM_DEFAULT); /* * Reserve the bootmem bitmap itself as well. We do this in two * steps (first step was init_bootmem()) because this catches * the (very unlikely) case of us accidentally initializing the * bootmem allocator with an invalid RAM area. */ - reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size); + reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size, + BOOTMEM_DEFAULT); #ifdef CONFIG_BLK_DEV_INITRD if (INITRD_START && INITRD_SIZE) { if (INITRD_START + INITRD_SIZE <= memory_end) { - reserve_bootmem(INITRD_START, INITRD_SIZE); + reserve_bootmem(INITRD_START, INITRD_SIZE, + BOOTMEM_DEFAULT); initrd_start = INITRD_START; initrd_end = initrd_start + INITRD_SIZE; } else { diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 1a2fdb6991d..a4d29025ddb 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -24,6 +24,7 @@ #include <linux/smp.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/seq_file.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/kdebug.h> @@ -218,41 +219,40 @@ void show_registers(struct pt_regs *regs) } /* This is called from fs/proc/array.c */ -char *task_show_regs(struct task_struct *task, char *buffer) +void task_show_regs(struct seq_file *m, struct task_struct *task) { struct pt_regs *regs; regs = task_pt_regs(task); - buffer += sprintf(buffer, "task: %p, ksp: %p\n", + seq_printf(m, "task: %p, ksp: %p\n", task, (void *)task->thread.ksp); - buffer += sprintf(buffer, "User PSW : %p %p\n", + seq_printf(m, "User PSW : %p %p\n", (void *) regs->psw.mask, (void *)regs->psw.addr); - buffer += sprintf(buffer, "User GPRS: " FOURLONG, + seq_printf(m, "User GPRS: " FOURLONG, regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]); - buffer += sprintf(buffer, " " FOURLONG, + seq_printf(m, " " FOURLONG, regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]); - buffer += sprintf(buffer, " " FOURLONG, + seq_printf(m, " " FOURLONG, regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]); - buffer += sprintf(buffer, " " FOURLONG, + seq_printf(m, " " FOURLONG, regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]); - buffer += sprintf(buffer, "User ACRS: %08x %08x %08x %08x\n", + seq_printf(m, "User ACRS: %08x %08x %08x %08x\n", task->thread.acrs[0], task->thread.acrs[1], task->thread.acrs[2], task->thread.acrs[3]); - buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + seq_printf(m, " %08x %08x %08x %08x\n", task->thread.acrs[4], task->thread.acrs[5], task->thread.acrs[6], task->thread.acrs[7]); - buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + seq_printf(m, " %08x %08x %08x %08x\n", task->thread.acrs[8], task->thread.acrs[9], task->thread.acrs[10], task->thread.acrs[11]); - buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + seq_printf(m, " %08x %08x %08x %08x\n", task->thread.acrs[12], task->thread.acrs[13], task->thread.acrs[14], task->thread.acrs[15]); - return buffer; } static DEFINE_SPINLOCK(die_lock); diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index e60e0ae1340..019f518cd5a 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -78,6 +78,7 @@ unsigned long *page_table_alloc(int noexec) clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE); page->index = (addr_t) table; } + pgtable_page_ctor(page); table = (unsigned long *) page_to_phys(page); clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE); return table; @@ -87,6 +88,7 @@ void page_table_free(unsigned long *table) { unsigned long *shadow = get_shadow_pte(table); + pgtable_page_dtor(virt_to_page(table)); if (shadow) free_page((unsigned long) shadow); free_page((unsigned long) table); diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 1c3a90835c7..6e035d1cf78 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -89,6 +89,9 @@ config ARCH_HAS_ILOG2_U64 config ARCH_NO_VIRT_TO_BUS def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + source "init/Kconfig" menu "System type" diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c index 3008c00eea6..8250e017bd4 100644 --- a/arch/sh/kernel/cpu/sh4/sq.c +++ b/arch/sh/kernel/cpu/sh4/sq.c @@ -263,7 +263,7 @@ struct sq_sysfs_attr { ssize_t (*store)(const char *buf, size_t count); }; -#define to_sq_sysfs_attr(attr) container_of(attr, struct sq_sysfs_attr, attr) +#define to_sq_sysfs_attr(a) container_of(a, struct sq_sysfs_attr, attr) static ssize_t sq_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 855cdf9d85b..18a5baf2cba 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -140,18 +140,26 @@ static void __init reserve_crashkernel(void) ret = parse_crashkernel(boot_command_line, free_mem, &crash_size, &crash_base); if (ret == 0 && crash_size) { - if (crash_base > 0) { - printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " - "for crashkernel (System RAM: %ldMB)\n", - (unsigned long)(crash_size >> 20), - (unsigned long)(crash_base >> 20), - (unsigned long)(free_mem >> 20)); - crashk_res.start = crash_base; - crashk_res.end = crash_base + crash_size - 1; - reserve_bootmem(crash_base, crash_size); - } else + if (crash_base <= 0) { printk(KERN_INFO "crashkernel reservation failed - " "you have to specify a base address\n"); + return; + } + + if (reserve_bootmem(crash_base, crash_size, + BOOTMEM_EXCLUSIVE) < 0) { + printk(KERN_INFO "crashkernel reservation failed - " + "memory is in use\n"); + return; + } + + printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " + "for crashkernel (System RAM: %ldMB)\n", + (unsigned long)(crash_size >> 20), + (unsigned long)(crash_base >> 20), + (unsigned long)(free_mem >> 20)); + crashk_res.start = crash_base; + crashk_res.end = crash_base + crash_size - 1; } } #else @@ -184,13 +192,14 @@ void __init setup_bootmem_allocator(unsigned long free_pfn) * an invalid RAM area. */ reserve_bootmem(__MEMORY_START+PAGE_SIZE, - (PFN_PHYS(free_pfn)+bootmap_size+PAGE_SIZE-1)-__MEMORY_START); + (PFN_PHYS(free_pfn)+bootmap_size+PAGE_SIZE-1)-__MEMORY_START, + BOOTMEM_DEFAULT); /* * reserve physical page 0 - it's a special BIOS page on many boxes, * enabling clean reboots, SMP operation, laptop functions. */ - reserve_bootmem(__MEMORY_START, PAGE_SIZE); + reserve_bootmem(__MEMORY_START, PAGE_SIZE, BOOTMEM_DEFAULT); sparse_memory_present_with_active_regions(0); @@ -200,7 +209,7 @@ void __init setup_bootmem_allocator(unsigned long free_pfn) if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { reserve_bootmem(INITRD_START + __MEMORY_START, - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET + __MEMORY_START; initrd_end = initrd_start + INITRD_SIZE; diff --git a/arch/sh/mm/numa.c b/arch/sh/mm/numa.c index 8aff065dd30..2de7302724f 100644 --- a/arch/sh/mm/numa.c +++ b/arch/sh/mm/numa.c @@ -80,9 +80,9 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) /* Reserve the pgdat and bootmap space with the bootmem allocator */ reserve_bootmem_node(NODE_DATA(nid), start_pfn << PAGE_SHIFT, - sizeof(struct pglist_data)); + sizeof(struct pglist_data), BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(nid), free_pfn << PAGE_SHIFT, - bootmap_pages << PAGE_SHIFT); + bootmap_pages << PAGE_SHIFT, BOOTMEM_DEFAULT); /* It's up */ node_set_online(nid); diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 99f8971716d..7c674a3503b 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -27,6 +27,13 @@ config ARCH_NO_VIRT_TO_BUS config OF def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + +config HZ + int + default 100 + source "init/Kconfig" menu "General machine setup" diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 88d2cefd01b..c2eed8f7151 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -1224,23 +1224,6 @@ sys_nis_syscall: call c_sys_nis_syscall mov %l5, %o7 - .align 4 - .globl sys_ptrace -sys_ptrace: - call do_ptrace - add %sp, STACKFRAME_SZ, %o0 - - ld [%curptr + TI_FLAGS], %l5 - andcc %l5, _TIF_SYSCALL_TRACE, %g0 - be 1f - nop - - call syscall_trace - nop - -1: - RESTORE_ALL - .align 4 .globl sys_execve sys_execve: diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 77460e316a0..19186ce8850 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -20,7 +20,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/smp.h> #include <linux/reboot.h> #include <linux/delay.h> @@ -567,38 +566,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, } /* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - unsigned long first_stack_page; - - dump->magic = SUNOS_CORE_MAGIC; - dump->len = sizeof(struct user); - dump->regs.psr = regs->psr; - dump->regs.pc = regs->pc; - dump->regs.npc = regs->npc; - dump->regs.y = regs->y; - /* fuck me plenty */ - memcpy(&dump->regs.regs[0], ®s->u_regs[1], (sizeof(unsigned long) * 15)); - dump->uexec = current->thread.core_exec; - dump->u_tsize = (((unsigned long) current->mm->end_code) - - ((unsigned long) current->mm->start_code)) & ~(PAGE_SIZE - 1); - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))); - dump->u_dsize -= dump->u_tsize; - dump->u_dsize &= ~(PAGE_SIZE - 1); - first_stack_page = (regs->u_regs[UREG_FP] & ~(PAGE_SIZE - 1)); - dump->u_ssize = (TASK_SIZE - first_stack_page) & ~(PAGE_SIZE - 1); - memcpy(&dump->fpu.fpstatus.fregs.regs[0], ¤t->thread.float_regs[0], (sizeof(unsigned long) * 32)); - dump->fpu.fpstatus.fsr = current->thread.fsr; - dump->fpu.fpstatus.flags = dump->fpu.fpstatus.extra = 0; - dump->fpu.fpstatus.fpq_count = current->thread.fpqdepth; - memcpy(&dump->fpu.fpstatus.fpq[0], ¤t->thread.fpqueue[0], - ((sizeof(unsigned long) * 2) * 16)); - dump->sigcode = 0; -} - -/* * fill in the fpu structure for a core dump. */ int dump_fpu (struct pt_regs * regs, elf_fpregset_t * fpregs) diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 7452269bba2..5b54f11f4e5 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -1,6 +1,6 @@ /* ptrace.c: Sparc process tracing support. * - * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) + * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) * * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, * and David Mosberger. @@ -19,389 +19,343 @@ #include <linux/smp_lock.h> #include <linux/security.h> #include <linux/signal.h> +#include <linux/regset.h> +#include <linux/elf.h> #include <asm/pgtable.h> #include <asm/system.h> #include <asm/uaccess.h> -#define MAGIC_CONSTANT 0x80000000 - +/* #define ALLOW_INIT_TRACING */ -/* Returning from ptrace is a bit tricky because the syscall return - * low level code assumes any value returned which is negative and - * is a valid errno will mean setting the condition codes to indicate - * an error return. This doesn't work, so we have this hook. +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. */ -static inline void pt_error_return(struct pt_regs *regs, unsigned long error) +void ptrace_disable(struct task_struct *child) { - regs->u_regs[UREG_I0] = error; - regs->psr |= PSR_C; - regs->pc = regs->npc; - regs->npc += 4; + /* nothing to do */ } -static inline void pt_succ_return(struct pt_regs *regs, unsigned long value) -{ - regs->u_regs[UREG_I0] = value; - regs->psr &= ~PSR_C; - regs->pc = regs->npc; - regs->npc += 4; -} +enum sparc_regset { + REGSET_GENERAL, + REGSET_FP, +}; -static void -pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr) +static int genregs32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - if (put_user(value, addr)) { - pt_error_return(regs, EFAULT); - return; + const struct pt_regs *regs = target->thread.kregs; + unsigned long __user *reg_window; + unsigned long *k = kbuf; + unsigned long __user *u = ubuf; + unsigned long reg; + + if (target == current) + flush_user_windows(); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) { + for (; count > 0 && pos < 16; count--) + *k++ = regs->u_regs[pos++]; + + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(*k++, ®_window[pos++])) + return -EFAULT; + } + } else { + for (; count > 0 && pos < 16; count--) { + if (put_user(regs->u_regs[pos++], u++)) + return -EFAULT; + } + + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(reg, ®_window[pos++]) || + put_user(reg, u++)) + return -EFAULT; + } } - regs->u_regs[UREG_I0] = 0; - regs->psr &= ~PSR_C; - regs->pc = regs->npc; - regs->npc += 4; -} + while (count > 0) { + switch (pos) { + case 32: /* PSR */ + reg = regs->psr; + break; + case 33: /* PC */ + reg = regs->pc; + break; + case 34: /* NPC */ + reg = regs->npc; + break; + case 35: /* Y */ + reg = regs->y; + break; + case 36: /* WIM */ + case 37: /* TBR */ + reg = 0; + break; + default: + goto finish; + } -static void -pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr) -{ - if (current->personality == PER_SUNOS) - pt_succ_return (regs, val); - else - pt_succ_return_linux (regs, val, addr); + if (kbuf) + *k++ = reg; + else if (put_user(reg, u++)) + return -EFAULT; + pos++; + count--; + } +finish: + pos *= sizeof(reg); + count *= sizeof(reg); + + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 38 * sizeof(reg), -1); } -/* Fuck me gently with a chainsaw... */ -static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset, - struct task_struct *tsk, long __user *addr) +static int genregs32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - struct pt_regs *cregs = tsk->thread.kregs; - struct thread_info *t = task_thread_info(tsk); - int v; - - if(offset >= 1024) - offset -= 1024; /* whee... */ - if(offset & ((sizeof(unsigned long) - 1))) { - pt_error_return(regs, EIO); - return; - } - if(offset >= 16 && offset < 784) { - offset -= 16; offset >>= 2; - pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr); - return; - } - if(offset >= 784 && offset < 832) { - offset -= 784; offset >>= 2; - pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr); - return; - } - switch(offset) { - case 0: - v = t->ksp; - break; - case 4: - v = t->kpc; - break; - case 8: - v = t->kpsr; - break; - case 12: - v = t->uwinmask; - break; - case 832: - v = t->w_saved; - break; - case 896: - v = cregs->u_regs[UREG_I0]; - break; - case 900: - v = cregs->u_regs[UREG_I1]; - break; - case 904: - v = cregs->u_regs[UREG_I2]; - break; - case 908: - v = cregs->u_regs[UREG_I3]; - break; - case 912: - v = cregs->u_regs[UREG_I4]; - break; - case 916: - v = cregs->u_regs[UREG_I5]; - break; - case 920: - v = cregs->u_regs[UREG_I6]; - break; - case 924: - if(tsk->thread.flags & MAGIC_CONSTANT) - v = cregs->u_regs[UREG_G1]; - else - v = 0; - break; - case 940: - v = cregs->u_regs[UREG_I0]; - break; - case 944: - v = cregs->u_regs[UREG_I1]; - break; + struct pt_regs *regs = target->thread.kregs; + unsigned long __user *reg_window; + const unsigned long *k = kbuf; + const unsigned long __user *u = ubuf; + unsigned long reg; + + if (target == current) + flush_user_windows(); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) { + for (; count > 0 && pos < 16; count--) + regs->u_regs[pos++] = *k++; + + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (put_user(*k++, ®_window[pos++])) + return -EFAULT; + } + } else { + for (; count > 0 && pos < 16; count--) { + if (get_user(reg, u++)) + return -EFAULT; + regs->u_regs[pos++] = reg; + } - case 948: - /* Isn't binary compatibility _fun_??? */ - if(cregs->psr & PSR_C) - v = cregs->u_regs[UREG_I0] << 24; - else - v = 0; - break; + reg_window = (unsigned long __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(reg, u++) || + put_user(reg, ®_window[pos++])) + return -EFAULT; + } + } + while (count > 0) { + unsigned long psr; + + if (kbuf) + reg = *k++; + else if (get_user(reg, u++)) + return -EFAULT; + + switch (pos) { + case 32: /* PSR */ + psr = regs->psr; + psr &= ~PSR_ICC; + psr |= (reg & PSR_ICC); + regs->psr = psr; + break; + case 33: /* PC */ + regs->pc = reg; + break; + case 34: /* NPC */ + regs->npc = reg; + break; + case 35: /* Y */ + regs->y = reg; + break; + case 36: /* WIM */ + case 37: /* TBR */ + break; + default: + goto finish; + } - /* Rest of them are completely unsupported. */ - default: - printk("%s [%d]: Wants to read user offset %ld\n", - current->comm, task_pid_nr(current), offset); - pt_error_return(regs, EIO); - return; + pos++; + count--; } - if (current->personality == PER_SUNOS) - pt_succ_return (regs, v); - else - pt_succ_return_linux (regs, v, addr); - return; +finish: + pos *= sizeof(reg); + count *= sizeof(reg); + + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 38 * sizeof(reg), -1); } -static inline void write_sunos_user(struct pt_regs *regs, unsigned long offset, - struct task_struct *tsk) +static int fpregs32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - struct pt_regs *cregs = tsk->thread.kregs; - struct thread_info *t = task_thread_info(tsk); - unsigned long value = regs->u_regs[UREG_I3]; - - if(offset >= 1024) - offset -= 1024; /* whee... */ - if(offset & ((sizeof(unsigned long) - 1))) - goto failure; - if(offset >= 16 && offset < 784) { - offset -= 16; offset >>= 2; - *(((unsigned long *)(&t->reg_window[0]))+offset) = value; - goto success; - } - if(offset >= 784 && offset < 832) { - offset -= 784; offset >>= 2; - *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset) = value; - goto success; - } - switch(offset) { - case 896: - cregs->u_regs[UREG_I0] = value; - break; - case 900: - cregs->u_regs[UREG_I1] = value; - break; - case 904: - cregs->u_regs[UREG_I2] = value; - break; - case 908: - cregs->u_regs[UREG_I3] = value; - break; - case 912: - cregs->u_regs[UREG_I4] = value; - break; - case 916: - cregs->u_regs[UREG_I5] = value; - break; - case 920: - cregs->u_regs[UREG_I6] = value; - break; - case 924: - cregs->u_regs[UREG_I7] = value; - break; - case 940: - cregs->u_regs[UREG_I0] = value; - break; - case 944: - cregs->u_regs[UREG_I1] = value; - break; + const unsigned long *fpregs = target->thread.float_regs; + int ret = 0; - /* Rest of them are completely unsupported or "no-touch". */ - default: - printk("%s [%d]: Wants to write user offset %ld\n", - current->comm, task_pid_nr(current), offset); - goto failure; +#if 0 + if (target == current) + save_and_clear_fpu(); +#endif + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 33 * sizeof(u32), + 34 * sizeof(u32)); + + if (!ret) { + unsigned long val; + + val = (1 << 8) | (8 << 16); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &val, + 34 * sizeof(u32), + 35 * sizeof(u32)); } -success: - pt_succ_return(regs, 0); - return; -failure: - pt_error_return(regs, EIO); - return; -} -/* #define ALLOW_INIT_TRACING */ -/* #define DEBUG_PTRACE */ - -#ifdef DEBUG_PTRACE -char *pt_rq [] = { - /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR", - /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT", - /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH", - /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS", - /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT", - /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown", - /* 24 */ "SYSCALL", "" -}; -#endif + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 35 * sizeof(u32), -1); -/* - * Called by kernel/ptrace.c when detaching.. - * - * Make sure single step bits etc are not set. - */ -void ptrace_disable(struct task_struct *child) -{ - /* nothing to do */ + return ret; } -asmlinkage void do_ptrace(struct pt_regs *regs) +static int fpregs32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - unsigned long request = regs->u_regs[UREG_I0]; - unsigned long pid = regs->u_regs[UREG_I1]; - unsigned long addr = regs->u_regs[UREG_I2]; - unsigned long data = regs->u_regs[UREG_I3]; - unsigned long addr2 = regs->u_regs[UREG_I4]; - struct task_struct *child; + unsigned long *fpregs = target->thread.float_regs; int ret; - lock_kernel(); -#ifdef DEBUG_PTRACE - { - char *s; - - if ((request >= 0) && (request <= 24)) - s = pt_rq [request]; - else - s = "unknown"; - - if (request == PTRACE_POKEDATA && data == 0x91d02001){ - printk ("do_ptrace: breakpoint pid=%d, addr=%08lx addr2=%08lx\n", - pid, addr, addr2); - } else - printk("do_ptrace: rq=%s(%d) pid=%d addr=%08lx data=%08lx addr2=%08lx\n", - s, (int) request, (int) pid, addr, data, addr2); - } +#if 0 + if (target == current) + save_and_clear_fpu(); #endif - - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - if (ret < 0) - pt_error_return(regs, -ret); - else - pt_succ_return(regs, 0); - goto out; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (!ret) + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fsr, + 33 * sizeof(u32), + 34 * sizeof(u32)); } - child = ptrace_get_task_struct(pid); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - pt_error_return(regs, -ret); - goto out; - } + if (!ret) + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 34 * sizeof(u32), -1); + return ret; +} - if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) - || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { - if (ptrace_attach(child)) { - pt_error_return(regs, EPERM); - goto out_tsk; - } - pt_succ_return(regs, 0); - goto out_tsk; - } +static const struct user_regset sparc32_regsets[] = { + /* Format is: + * G0 --> G7 + * O0 --> O7 + * L0 --> L7 + * I0 --> I7 + * PSR, PC, nPC, Y, WIM, TBR + */ + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = 38 * sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .get = genregs32_get, .set = genregs32_set + }, + /* Format is: + * F0 --> F31 + * empty 32-bit word + * FSR (32--bit word) + * FPU QUEUE COUNT (8-bit char) + * FPU QUEUE ENTRYSIZE (8-bit char) + * FPU ENABLED (8-bit char) + * empty 8-bit char + * FPU QUEUE (64 32-bit ints) + */ + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, + .n = 99 * sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .get = fpregs32_get, .set = fpregs32_set + }, +}; - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) { - pt_error_return(regs, -ret); - goto out_tsk; - } +static const struct user_regset_view user_sparc32_view = { + .name = "sparc", .e_machine = EM_SPARC, + .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) +}; - switch(request) { - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp; - - if (access_process_vm(child, addr, - &tmp, sizeof(tmp), 0) == sizeof(tmp)) - pt_os_succ_return(regs, tmp, (long __user *)data); - else - pt_error_return(regs, EIO); - goto out_tsk; - } +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &user_sparc32_view; +} - case PTRACE_PEEKUSR: - read_sunos_user(regs, addr, child, (long __user *) data); - goto out_tsk; - - case PTRACE_POKEUSR: - write_sunos_user(regs, addr, child); - goto out_tsk; - - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: { - if (access_process_vm(child, addr, - &data, sizeof(data), 1) == sizeof(data)) - pt_succ_return(regs, 0); - else - pt_error_return(regs, EIO); - goto out_tsk; - } +long arch_ptrace(struct task_struct *child, long request, long addr, long data) +{ + unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; + const struct user_regset_view *view; + int ret; + + view = task_user_regset_view(child); + switch(request) { case PTRACE_GETREGS: { struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - struct pt_regs *cregs = child->thread.kregs; - int rval; - if (!access_ok(VERIFY_WRITE, pregs, sizeof(struct pt_regs))) { - rval = -EFAULT; - pt_error_return(regs, -rval); - goto out_tsk; - } - __put_user(cregs->psr, (&pregs->psr)); - __put_user(cregs->pc, (&pregs->pc)); - __put_user(cregs->npc, (&pregs->npc)); - __put_user(cregs->y, (&pregs->y)); - for(rval = 1; rval < 16; rval++) - __put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1])); - pt_succ_return(regs, 0); -#ifdef DEBUG_PTRACE - printk ("PC=%x nPC=%x o7=%x\n", cregs->pc, cregs->npc, cregs->u_regs [15]); -#endif - goto out_tsk; + ret = copy_regset_to_user(child, view, REGSET_GENERAL, + 32 * sizeof(u32), + 4 * sizeof(u32), + &pregs->psr); + if (!ret) + copy_regset_to_user(child, view, REGSET_GENERAL, + 1 * sizeof(u32), + 15 * sizeof(u32), + &pregs->u_regs[0]); + break; } case PTRACE_SETREGS: { struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - struct pt_regs *cregs = child->thread.kregs; - unsigned long psr, pc, npc, y; - int i; - - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (!access_ok(VERIFY_READ, pregs, sizeof(struct pt_regs))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - __get_user(psr, (&pregs->psr)); - __get_user(pc, (&pregs->pc)); - __get_user(npc, (&pregs->npc)); - __get_user(y, (&pregs->y)); - psr &= PSR_ICC; - cregs->psr &= ~PSR_ICC; - cregs->psr |= psr; - if (!((pc | npc) & 3)) { - cregs->pc = pc; - cregs->npc =npc; - } - cregs->y = y; - for(i = 1; i < 16; i++) - __get_user(cregs->u_regs[i], (&pregs->u_regs[i-1])); - pt_succ_return(regs, 0); - goto out_tsk; + + ret = copy_regset_from_user(child, view, REGSET_GENERAL, + 32 * sizeof(u32), + 4 * sizeof(u32), + &pregs->psr); + if (!ret) + copy_regset_from_user(child, view, REGSET_GENERAL, + 1 * sizeof(u32), + 15 * sizeof(u32), + &pregs->u_regs[0]); + break; } case PTRACE_GETFPREGS: { @@ -417,26 +371,25 @@ asmlinkage void do_ptrace(struct pt_regs *regs) } fpq[16]; }; struct fps __user *fps = (struct fps __user *) addr; - int i; - if (!access_ok(VERIFY_WRITE, fps, sizeof(struct fps))) { - i = -EFAULT; - pt_error_return(regs, -i); - goto out_tsk; - } - for(i = 0; i < 32; i++) - __put_user(child->thread.float_regs[i], (&fps->regs[i])); - __put_user(child->thread.fsr, (&fps->fsr)); - __put_user(child->thread.fpqdepth, (&fps->fpqd)); - __put_user(0, (&fps->flags)); - __put_user(0, (&fps->extra)); - for(i = 0; i < 16; i++) { - __put_user(child->thread.fpqueue[i].insn_addr, - (&fps->fpq[i].insnaddr)); - __put_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); + ret = copy_regset_to_user(child, view, REGSET_FP, + 0 * sizeof(u32), + 32 * sizeof(u32), + &fps->regs[0]); + if (!ret) + ret = copy_regset_to_user(child, view, REGSET_FP, + 33 * sizeof(u32), + 1 * sizeof(u32), + &fps->fsr); + + if (!ret) { + if (__put_user(0, &fps->fpqd) || + __put_user(0, &fps->flags) || + __put_user(0, &fps->extra) || + clear_user(fps->fpq, sizeof(fps->fpq))) + ret = -EFAULT; } - pt_succ_return(regs, 0); - goto out_tsk; + break; } case PTRACE_SETFPREGS: { @@ -452,137 +405,55 @@ asmlinkage void do_ptrace(struct pt_regs *regs) } fpq[16]; }; struct fps __user *fps = (struct fps __user *) addr; - int i; - if (!access_ok(VERIFY_READ, fps, sizeof(struct fps))) { - i = -EFAULT; - pt_error_return(regs, -i); - goto out_tsk; - } - copy_from_user(&child->thread.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned long))); - __get_user(child->thread.fsr, (&fps->fsr)); - __get_user(child->thread.fpqdepth, (&fps->fpqd)); - for(i = 0; i < 16; i++) { - __get_user(child->thread.fpqueue[i].insn_addr, - (&fps->fpq[i].insnaddr)); - __get_user(child->thread.fpqueue[i].insn, (&fps->fpq[i].insn)); - } - pt_succ_return(regs, 0); - goto out_tsk; + ret = copy_regset_from_user(child, view, REGSET_FP, + 0 * sizeof(u32), + 32 * sizeof(u32), + &fps->regs[0]); + if (!ret) + ret = copy_regset_from_user(child, view, REGSET_FP, + 33 * sizeof(u32), + 1 * sizeof(u32), + &fps->fsr); + break; } case PTRACE_READTEXT: - case PTRACE_READDATA: { - int res = ptrace_readdata(child, addr, - (void __user *) addr2, data); - - if (res == data) { - pt_succ_return(regs, 0); - goto out_tsk; - } - /* Partial read is an IO failure */ - if (res >= 0) - res = -EIO; - pt_error_return(regs, -res); - goto out_tsk; - } + case PTRACE_READDATA: + ret = ptrace_readdata(child, addr, + (void __user *) addr2, data); + + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; + break; case PTRACE_WRITETEXT: - case PTRACE_WRITEDATA: { - int res = ptrace_writedata(child, (void __user *) addr2, - addr, data); - - if (res == data) { - pt_succ_return(regs, 0); - goto out_tsk; - } - /* Partial write is an IO failure */ - if (res >= 0) - res = -EIO; - pt_error_return(regs, -res); - goto out_tsk; - } - - case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ - addr = 1; - - case PTRACE_CONT: { /* restart after signal. */ - if (!valid_signal(data)) { - pt_error_return(regs, EIO); - goto out_tsk; - } - - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - - child->exit_code = data; -#ifdef DEBUG_PTRACE - printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", - child->comm, child->pid, child->exit_code, - child->thread.kregs->pc, - child->thread.kregs->npc); -#endif - wake_up_process(child); - pt_succ_return(regs, 0); - goto out_tsk; - } - -/* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: { - if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ - pt_succ_return(regs, 0); - goto out_tsk; - } - wake_up_process(child); - child->exit_code = SIGKILL; - pt_succ_return(regs, 0); - goto out_tsk; - } + case PTRACE_WRITEDATA: + ret = ptrace_writedata(child, (void __user *) addr2, + addr, data); + + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; + break; - case PTRACE_SUNDETACH: { /* detach a process that was attached. */ - int err = ptrace_detach(child, data); - if (err) { - pt_error_return(regs, EIO); - goto out_tsk; - } - pt_succ_return(regs, 0); - goto out_tsk; + default: + ret = ptrace_request(child, request, addr, data); + break; } - /* PTRACE_DUMPCORE unsupported... */ - - default: { - int err = ptrace_request(child, request, addr, data); - if (err) - pt_error_return(regs, -err); - else - pt_succ_return(regs, 0); - goto out_tsk; - } - } -out_tsk: - if (child) - put_task_struct(child); -out: - unlock_kernel(); + return ret; } asmlinkage void syscall_trace(void) { -#ifdef DEBUG_PTRACE - printk("%s [%d]: syscall_trace\n", current->comm, current->pid); -#endif if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; if (!(current->ptrace & PT_PTRACED)) return; - current->thread.flags ^= MAGIC_CONSTANT; ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); /* @@ -590,10 +461,6 @@ asmlinkage void syscall_trace(void) * for normal use. strace only continues with a signal if the * stopping signal is not SIGTRAP. -brl */ -#ifdef DEBUG_PTRACE - printk("%s [%d]: syscall_trace exit= %x\n", current->comm, - current->pid, current->exit_code); -#endif if (current->exit_code) { send_sig (current->exit_code, current, 1); current->exit_code = 0; diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index d07bc74773a..3cf78f16084 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -16,7 +16,6 @@ #include <linux/initrd.h> #include <asm/smp.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/delay.h> #include <linux/fs.h> diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index ef647acc479..c1025e55165 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -51,7 +51,6 @@ #ifdef CONFIG_PCI #include <asm/ebus.h> #endif -#include <asm/a.out.h> #include <asm/io-unit.h> #include <asm/bug.h> @@ -214,8 +213,6 @@ EXPORT_SYMBOL(kunmap_atomic); EXPORT_SYMBOL(svr4_setcontext); EXPORT_SYMBOL(svr4_getcontext); -EXPORT_SYMBOL(dump_thread); - /* prom symbols */ EXPORT_SYMBOL(idprom); EXPORT_SYMBOL(prom_root_node); diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index a1bef07755a..b89837accc8 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -259,7 +259,7 @@ unsigned long __init bootmem_init(unsigned long *pages_avail) if (initrd_start) { /* Reserve the initrd image area. */ size = initrd_end - initrd_start; - reserve_bootmem(initrd_start, size); + reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; initrd_start = (initrd_start - phys_base) + PAGE_OFFSET; @@ -268,7 +268,7 @@ unsigned long __init bootmem_init(unsigned long *pages_avail) #endif /* Reserve the kernel text/data/bss. */ size = (start_pfn << PAGE_SHIFT) - phys_base; - reserve_bootmem(phys_base, size); + reserve_bootmem(phys_base, size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; /* Reserve the bootmem map. We do not account for it @@ -276,7 +276,7 @@ unsigned long __init bootmem_init(unsigned long *pages_avail) * in free_all_bootmem. */ size = bootmap_size; - reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size); + reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; return max_pfn; diff --git a/arch/sparc/mm/loadmmu.c b/arch/sparc/mm/loadmmu.c index 36b4d24988f..2d9cd65160a 100644 --- a/arch/sparc/mm/loadmmu.c +++ b/arch/sparc/mm/loadmmu.c @@ -14,7 +14,6 @@ #include <asm/system.h> #include <asm/page.h> #include <asm/pgtable.h> -#include <asm/a.out.h> #include <asm/mmu_context.h> #include <asm/oplib.h> diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 17b485f2825..23d3291a3e8 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -34,7 +34,6 @@ #include <asm/sbus.h> #include <asm/asi.h> #include <asm/msi.h> -#include <asm/a.out.h> #include <asm/mmu_context.h> #include <asm/io-unit.h> #include <asm/cacheflush.h> @@ -490,14 +489,17 @@ srmmu_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return (pte_t *)srmmu_get_nocache(PTE_SIZE, PTE_SIZE); } -static struct page * +static pgtable_t srmmu_pte_alloc_one(struct mm_struct *mm, unsigned long address) { unsigned long pte; + struct page *page; if ((pte = (unsigned long)srmmu_pte_alloc_one_kernel(mm, address)) == 0) return NULL; - return pfn_to_page( __nocache_pa(pte) >> PAGE_SHIFT ); + page = pfn_to_page( __nocache_pa(pte) >> PAGE_SHIFT ); + pgtable_page_ctor(page); + return page; } static void srmmu_free_pte_fast(pte_t *pte) @@ -505,10 +507,11 @@ static void srmmu_free_pte_fast(pte_t *pte) srmmu_free_nocache((unsigned long)pte, PTE_SIZE); } -static void srmmu_pte_free(struct page *pte) +static void srmmu_pte_free(pgtable_t pte) { unsigned long p; + pgtable_page_dtor(pte); p = (unsigned long)page_address(pte); /* Cached address (for test) */ if (p == 0) BUG(); diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index 0729305f2f5..c0442e8c4b1 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1947,12 +1947,17 @@ static pte_t *sun4c_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long add return pte; } -static struct page *sun4c_pte_alloc_one(struct mm_struct *mm, unsigned long address) +static pgtable_t sun4c_pte_alloc_one(struct mm_struct *mm, unsigned long address) { - pte_t *pte = sun4c_pte_alloc_one_kernel(mm, address); + pte_t *pte; + struct page *page; + + pte = sun4c_pte_alloc_one_kernel(mm, address); if (pte == NULL) return NULL; - return virt_to_page(pte); + page = virt_to_page(pte); + pgtable_page_ctor(page); + return page; } static inline void sun4c_free_pte_fast(pte_t *pte) @@ -1962,8 +1967,9 @@ static inline void sun4c_free_pte_fast(pte_t *pte) pgtable_cache_size++; } -static void sun4c_pte_free(struct page *pte) +static void sun4c_pte_free(pgtable_t pte) { + pgtable_page_dtor(pte); sun4c_free_pte_fast(page_address(pte)); } diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index a8c6366f05a..b810f2b7526 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -81,6 +81,9 @@ config GENERIC_HARDIRQS_NO__DO_IRQ bool def_bool y +config ARCH_SUPPORTS_AOUT + def_bool y + choice prompt "Kernel page size" default SPARC64_PAGE_SIZE_8KB @@ -380,17 +383,11 @@ config COMPAT bool depends on SPARC32_COMPAT default y - -config BINFMT_ELF32 - bool "Kernel support for 32-bit ELF binaries" - depends on SPARC32_COMPAT - help - This allows you to run 32-bit Linux/ELF binaries on your Ultra. - Everybody wants this; say Y. + select COMPAT_BINFMT_ELF config BINFMT_AOUT32 bool "Kernel support for 32-bit (ie. SunOS) a.out binaries" - depends on SPARC32_COMPAT + depends on SPARC32_COMPAT && ARCH_SUPPORTS_AOUT help This allows you to run 32-bit a.out format binaries on your Ultra. If you want to run SunOS binaries (see SunOS binary emulation below) diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 4b78b24ef41..1bf5b187de4 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o \ obj-$(CONFIG_PCI_MSI) += pci_msi.o obj-$(CONFIG_SMP) += smp.o trampoline.o hvtramp.o obj-$(CONFIG_SPARC32_COMPAT) += sys32.o sys_sparc32.o signal32.o -obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o obj-$(CONFIG_BINFMT_AOUT32) += binfmt_aout32.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index 92c1b36a2e1..9877f2d7672 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -32,6 +32,7 @@ #include <asm/uaccess.h> #include <asm/pgalloc.h> #include <asm/mmu_context.h> +#include <asm/a.out-core.h> static int load_aout32_binary(struct linux_binprm *, struct pt_regs * regs); static int load_aout32_library(struct file*); @@ -101,7 +102,7 @@ static int aout32_core_dump(long signr, struct pt_regs *regs, struct file *file, current->flags |= PF_DUMPCORE; strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm)); dump.signal = signr; - dump_thread(regs, &dump); + aout_dump_thread(regs, &dump); /* If the size of the dump file exceeds the rlimit, then see what would happen if we wrote the stack, but not the data area. */ diff --git a/arch/sparc64/kernel/binfmt_elf32.c b/arch/sparc64/kernel/binfmt_elf32.c deleted file mode 100644 index 1587a29a4b0..00000000000 --- a/arch/sparc64/kernel/binfmt_elf32.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * binfmt_elf32.c: Support 32-bit Sparc ELF binaries on Ultra. - * - * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@davemloft.net) - * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) - */ - -#define ELF_ARCH EM_SPARC -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB; - -/* For the most part we present code dumps in the format - * Solaris does. - */ -typedef unsigned int elf_greg_t; -#define ELF_NGREG 38 -typedef elf_greg_t elf_gregset_t[ELF_NGREG]; - -/* Format is: - * G0 --> G7 - * O0 --> O7 - * L0 --> L7 - * I0 --> I7 - * PSR, PC, nPC, Y, WIM, TBR - */ -#include <asm/psrcompat.h> -#define ELF_CORE_COPY_REGS(__elf_regs, __pt_regs) \ -do { unsigned int *dest = &(__elf_regs[0]); \ - struct pt_regs *src = (__pt_regs); \ - unsigned int __user *sp; \ - int i; \ - for(i = 0; i < 16; i++) \ - dest[i] = (unsigned int) src->u_regs[i];\ - /* Don't try this at home kids... */ \ - sp = (unsigned int __user *) (src->u_regs[14] & \ - 0x00000000fffffffc); \ - for(i = 0; i < 16; i++) \ - __get_user(dest[i+16], &sp[i]); \ - dest[32] = tstate_to_psr(src->tstate); \ - dest[33] = (unsigned int) src->tpc; \ - dest[34] = (unsigned int) src->tnpc; \ - dest[35] = src->y; \ - dest[36] = dest[37] = 0; /* XXX */ \ -} while(0); - -typedef struct { - union { - unsigned int pr_regs[32]; - unsigned long pr_dregs[16]; - } pr_fr; - unsigned int __unused; - unsigned int pr_fsr; - unsigned char pr_qcnt; - unsigned char pr_q_entrysize; - unsigned char pr_en; - unsigned int pr_q[64]; -} elf_fpregset_t; - -/* UltraSparc extensions. Still unused, but will be eventually. */ -typedef struct { - unsigned int pr_type; - unsigned int pr_align; - union { - struct { - union { - unsigned int pr_regs[32]; - unsigned long pr_dregs[16]; - long double pr_qregs[8]; - } pr_xfr; - } pr_v8p; - unsigned int pr_xfsr; - unsigned int pr_fprs; - unsigned int pr_xg[8]; - unsigned int pr_xo[8]; - unsigned long pr_tstate; - unsigned int pr_filler[8]; - } pr_un; -} elf_xregset_t; - -#define elf_check_arch(x) (((x)->e_machine == EM_SPARC) || ((x)->e_machine == EM_SPARC32PLUS)) - -#define ELF_ET_DYN_BASE 0x70000000 - - -#include <asm/processor.h> -#include <linux/module.h> -#include <linux/elfcore.h> -#include <linux/compat.h> - -#define elf_prstatus elf_prstatus32 -struct elf_prstatus32 -{ - struct elf_siginfo pr_info; /* Info associated with signal */ - short pr_cursig; /* Current signal */ - unsigned int pr_sigpend; /* Set of pending signals */ - unsigned int pr_sighold; /* Set of held signals */ - pid_t pr_pid; - pid_t pr_ppid; - pid_t pr_pgrp; - pid_t pr_sid; - struct compat_timeval pr_utime; /* User time */ - struct compat_timeval pr_stime; /* System time */ - struct compat_timeval pr_cutime; /* Cumulative user time */ - struct compat_timeval pr_cstime; /* Cumulative system time */ - elf_gregset_t pr_reg; /* GP registers */ - int pr_fpvalid; /* True if math co-processor being used. */ -}; - -#define elf_prpsinfo elf_prpsinfo32 -struct elf_prpsinfo32 -{ - char pr_state; /* numeric process state */ - char pr_sname; /* char for pr_state */ - char pr_zomb; /* zombie */ - char pr_nice; /* nice val */ - unsigned int pr_flag; /* flags */ - u16 pr_uid; - u16 pr_gid; - pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; - /* Lots missing */ - char pr_fname[16]; /* filename of executable */ - char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ -}; - -#include <linux/highuid.h> - -#undef NEW_TO_OLD_UID -#undef NEW_TO_OLD_GID -#define NEW_TO_OLD_UID(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) -#define NEW_TO_OLD_GID(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) - -#include <linux/time.h> - -#undef cputime_to_timeval -#define cputime_to_timeval cputime_to_compat_timeval -static inline void -cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) -{ - unsigned long jiffies = cputime_to_jiffies(cputime); - value->tv_usec = (jiffies % HZ) * (1000000L / HZ); - value->tv_sec = jiffies / HZ; -} - -#undef start_thread -#define start_thread start_thread32 -#define init_elf_binfmt init_elf32_binfmt - -MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit SparcLinux binaries on the Ultra"); -MODULE_AUTHOR("Eric Youngdale, David S. Miller, Jakub Jelinek"); - -#undef MODULE_DESCRIPTION -#undef MODULE_AUTHOR - -#include <asm/a.out.h> - -#undef TASK_SIZE -#define TASK_SIZE STACK_TOP32 - -#include "../../../fs/binfmt_elf.c" diff --git a/arch/sparc64/kernel/entry.S b/arch/sparc64/kernel/entry.S index ea257e82836..6be4d2d2904 100644 --- a/arch/sparc64/kernel/entry.S +++ b/arch/sparc64/kernel/entry.S @@ -1477,10 +1477,6 @@ sys32_rt_sigreturn: add %o7, 1f-.-4, %o7 nop #endif -sys_ptrace: add %sp, PTREGS_OFF, %o0 - call do_ptrace - add %o7, 1f-.-4, %o7 - nop .align 32 1: ldx [%curptr + TI_FLAGS], %l5 andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0 diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index c4147ad8677..44b105c04dd 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -632,11 +632,36 @@ tlb_fixup_done: /* Not reached... */ 1: + /* If we boot on a non-zero cpu, all of the per-cpu + * variable references we make before setting up the + * per-cpu areas will use a bogus offset. Put a + * compensating factor into __per_cpu_base to handle + * this cleanly. + * + * What the per-cpu code calculates is: + * + * __per_cpu_base + (cpu << __per_cpu_shift) + * + * These two variables are zero initially, so to + * make it all cancel out to zero we need to put + * "0 - (cpu << 0)" into __per_cpu_base so that the + * above formula evaluates to zero. + * + * We cannot even perform a printk() until this stuff + * is setup as that calls cpu_clock() which uses + * per-cpu variables. + */ + sub %g0, %o0, %o1 + sethi %hi(__per_cpu_base), %o2 + stx %o1, [%o2 + %lo(__per_cpu_base)] #else mov 0, %o0 #endif sth %o0, [%g6 + TI_CPU] + call prom_init_report + nop + /* Off we go.... */ call start_kernel nop diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 30431bd24e1..5ec06c8c7fe 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -522,7 +522,7 @@ static struct irq_chip sun4v_virq = { .set_affinity = sun4v_virt_set_affinity, }; -static void fastcall pre_flow_handler(unsigned int virt_irq, +static void pre_flow_handler(unsigned int virt_irq, struct irq_desc *desc) { struct irq_handler_data *data = get_irq_chip_data(virt_irq); diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index ca7cdfd55f7..1b237917498 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -24,7 +24,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/reboot.h> #include <linux/delay.h> #include <linux/compat.h> @@ -725,17 +724,6 @@ pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) return retval; } -/* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - /* Only should be used for SunOS and ancient a.out - * SparcLinux binaries... Not worth implementing. - */ - memset(dump, 0, sizeof(struct user)); -} - typedef struct { union { unsigned int pr_regs[32]; diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 81111a12f0a..9a1ba1fe859 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -1,6 +1,6 @@ /* ptrace.c: Sparc process tracing support. * - * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) + * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, @@ -22,6 +22,9 @@ #include <linux/seccomp.h> #include <linux/audit.h> #include <linux/signal.h> +#include <linux/regset.h> +#include <linux/compat.h> +#include <linux/elf.h> #include <asm/asi.h> #include <asm/pgtable.h> @@ -33,70 +36,7 @@ #include <asm/page.h> #include <asm/cpudata.h> -/* Returning from ptrace is a bit tricky because the syscall return - * low level code assumes any value returned which is negative and - * is a valid errno will mean setting the condition codes to indicate - * an error return. This doesn't work, so we have this hook. - */ -static inline void pt_error_return(struct pt_regs *regs, unsigned long error) -{ - regs->u_regs[UREG_I0] = error; - regs->tstate |= (TSTATE_ICARRY | TSTATE_XCARRY); - regs->tpc = regs->tnpc; - regs->tnpc += 4; -} - -static inline void pt_succ_return(struct pt_regs *regs, unsigned long value) -{ - regs->u_regs[UREG_I0] = value; - regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY); - regs->tpc = regs->tnpc; - regs->tnpc += 4; -} - -static inline void -pt_succ_return_linux(struct pt_regs *regs, unsigned long value, void __user *addr) -{ - if (test_thread_flag(TIF_32BIT)) { - if (put_user(value, (unsigned int __user *) addr)) { - pt_error_return(regs, EFAULT); - return; - } - } else { - if (put_user(value, (long __user *) addr)) { - pt_error_return(regs, EFAULT); - return; - } - } - regs->u_regs[UREG_I0] = 0; - regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY); - regs->tpc = regs->tnpc; - regs->tnpc += 4; -} - -static void -pt_os_succ_return (struct pt_regs *regs, unsigned long val, void __user *addr) -{ - if (current->personality == PER_SUNOS) - pt_succ_return (regs, val); - else - pt_succ_return_linux (regs, val, addr); -} - /* #define ALLOW_INIT_TRACING */ -/* #define DEBUG_PTRACE */ - -#ifdef DEBUG_PTRACE -char *pt_rq [] = { - /* 0 */ "TRACEME", "PEEKTEXT", "PEEKDATA", "PEEKUSR", - /* 4 */ "POKETEXT", "POKEDATA", "POKEUSR", "CONT", - /* 8 */ "KILL", "SINGLESTEP", "SUNATTACH", "SUNDETACH", - /* 12 */ "GETREGS", "SETREGS", "GETFPREGS", "SETFPREGS", - /* 16 */ "READDATA", "WRITEDATA", "READTEXT", "WRITETEXT", - /* 20 */ "GETFPAREGS", "SETFPAREGS", "unknown", "unknown", - /* 24 */ "SYSCALL", "" -}; -#endif /* * Called by kernel/ptrace.c when detaching.. @@ -167,481 +107,784 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, } } -asmlinkage void do_ptrace(struct pt_regs *regs) +enum sparc_regset { + REGSET_GENERAL, + REGSET_FP, +}; + +static int genregs64_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - int request = regs->u_regs[UREG_I0]; - pid_t pid = regs->u_regs[UREG_I1]; - unsigned long addr = regs->u_regs[UREG_I2]; - unsigned long data = regs->u_regs[UREG_I3]; - unsigned long addr2 = regs->u_regs[UREG_I4]; - struct task_struct *child; + const struct pt_regs *regs = task_pt_regs(target); int ret; - if (test_thread_flag(TIF_32BIT)) { - addr &= 0xffffffffUL; - data &= 0xffffffffUL; - addr2 &= 0xffffffffUL; + if (target == current) + flushw_user(); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + regs->u_regs, + 0, 16 * sizeof(u64)); + if (!ret) { + unsigned long __user *reg_window = (unsigned long __user *) + (regs->u_regs[UREG_I6] + STACK_BIAS); + unsigned long window[16]; + + if (copy_from_user(window, reg_window, sizeof(window))) + return -EFAULT; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + window, + 16 * sizeof(u64), + 32 * sizeof(u64)); } - lock_kernel(); -#ifdef DEBUG_PTRACE - { - char *s; - if ((request >= 0) && (request <= 24)) - s = pt_rq [request]; - else - s = "unknown"; - - if (request == PTRACE_POKEDATA && data == 0x91d02001){ - printk ("do_ptrace: breakpoint pid=%d, addr=%016lx addr2=%016lx\n", - pid, addr, addr2); - } else - printk("do_ptrace: rq=%s(%d) pid=%d addr=%016lx data=%016lx addr2=%016lx\n", - s, request, pid, addr, data, addr2); + if (!ret) { + /* TSTATE, TPC, TNPC */ + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + ®s->tstate, + 32 * sizeof(u64), + 35 * sizeof(u64)); } -#endif - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - if (ret < 0) - pt_error_return(regs, -ret); - else - pt_succ_return(regs, 0); - goto out; + + if (!ret) { + unsigned long y = regs->y; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &y, + 35 * sizeof(u64), + 36 * sizeof(u64)); } - child = ptrace_get_task_struct(pid); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - pt_error_return(regs, -ret); - goto out; + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 36 * sizeof(u64), -1); + + return ret; +} + +static int genregs64_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + int ret; + + if (target == current) + flushw_user(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + regs->u_regs, + 0, 16 * sizeof(u64)); + if (!ret && count > 0) { + unsigned long __user *reg_window = (unsigned long __user *) + (regs->u_regs[UREG_I6] + STACK_BIAS); + unsigned long window[16]; + + if (copy_from_user(window, reg_window, sizeof(window))) + return -EFAULT; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + window, + 16 * sizeof(u64), + 32 * sizeof(u64)); + if (!ret && + copy_to_user(reg_window, window, sizeof(window))) + return -EFAULT; } - if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) - || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { - if (ptrace_attach(child)) { - pt_error_return(regs, EPERM); - goto out_tsk; + if (!ret && count > 0) { + unsigned long tstate; + + /* TSTATE */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &tstate, + 32 * sizeof(u64), + 33 * sizeof(u64)); + if (!ret) { + /* Only the condition codes can be modified + * in the %tstate register. + */ + tstate &= (TSTATE_ICC | TSTATE_XCC); + regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); + regs->tstate |= tstate; } - pt_succ_return(regs, 0); - goto out_tsk; } - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) { - pt_error_return(regs, -ret); - goto out_tsk; + if (!ret) { + /* TPC, TNPC */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s->tpc, + 33 * sizeof(u64), + 35 * sizeof(u64)); } - if (!(test_thread_flag(TIF_32BIT)) && - ((request == PTRACE_READDATA64) || - (request == PTRACE_WRITEDATA64) || - (request == PTRACE_READTEXT64) || - (request == PTRACE_WRITETEXT64) || - (request == PTRACE_PEEKTEXT64) || - (request == PTRACE_POKETEXT64) || - (request == PTRACE_PEEKDATA64) || - (request == PTRACE_POKEDATA64))) { - addr = regs->u_regs[UREG_G2]; - addr2 = regs->u_regs[UREG_G3]; - request -= 30; /* wheee... */ - } + if (!ret) { + unsigned long y; - switch(request) { - case PTRACE_PEEKUSR: - if (addr != 0) - pt_error_return(regs, EIO); - else - pt_succ_return(regs, 0); - goto out_tsk; - - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned long tmp64; - unsigned int tmp32; - int res, copied; - - res = -EIO; - if (test_thread_flag(TIF_32BIT)) { - copied = access_process_vm(child, addr, - &tmp32, sizeof(tmp32), 0); - tmp64 = (unsigned long) tmp32; - if (copied == sizeof(tmp32)) - res = 0; - } else { - copied = access_process_vm(child, addr, - &tmp64, sizeof(tmp64), 0); - if (copied == sizeof(tmp64)) - res = 0; - } - if (res < 0) - pt_error_return(regs, -res); - else - pt_os_succ_return(regs, tmp64, (void __user *) data); - goto out_tsk; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &y, + 35 * sizeof(u64), + 36 * sizeof(u64)); + if (!ret) + regs->y = y; } - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: { - unsigned long tmp64; - unsigned int tmp32; - int copied, res = -EIO; - - if (test_thread_flag(TIF_32BIT)) { - tmp32 = data; - copied = access_process_vm(child, addr, - &tmp32, sizeof(tmp32), 1); - if (copied == sizeof(tmp32)) - res = 0; - } else { - tmp64 = data; - copied = access_process_vm(child, addr, - &tmp64, sizeof(tmp64), 1); - if (copied == sizeof(tmp64)) - res = 0; - } - if (res < 0) - pt_error_return(regs, -res); + if (!ret) + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 36 * sizeof(u64), -1); + + return ret; +} + +static int fpregs64_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *fpregs = task_thread_info(target)->fpregs; + unsigned long fprs, fsr, gsr; + int ret; + + if (target == current) + save_and_clear_fpu(); + + fprs = task_thread_info(target)->fpsaved[0]; + + if (fprs & FPRS_DL) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 16 * sizeof(u64)); + else + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 0, + 16 * sizeof(u64)); + + if (!ret) { + if (fprs & FPRS_DU) + ret = user_regset_copyout(&pos, &count, + &kbuf, &ubuf, + fpregs + 16, + 16 * sizeof(u64), + 32 * sizeof(u64)); else - pt_succ_return(regs, res); - goto out_tsk; + ret = user_regset_copyout_zero(&pos, &count, + &kbuf, &ubuf, + 16 * sizeof(u64), + 32 * sizeof(u64)); } - case PTRACE_GETREGS: { - struct pt_regs32 __user *pregs = - (struct pt_regs32 __user *) addr; - struct pt_regs *cregs = task_pt_regs(child); - int rval; - - if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) || - __put_user(cregs->tpc, (&pregs->pc)) || - __put_user(cregs->tnpc, (&pregs->npc)) || - __put_user(cregs->y, (&pregs->y))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - for (rval = 1; rval < 16; rval++) - if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - pt_succ_return(regs, 0); -#ifdef DEBUG_PTRACE - printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); -#endif - goto out_tsk; + if (fprs & FPRS_FEF) { + fsr = task_thread_info(target)->xfsr[0]; + gsr = task_thread_info(target)->gsr[0]; + } else { + fsr = gsr = 0; } - case PTRACE_GETREGS64: { - struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - struct pt_regs *cregs = task_pt_regs(child); - unsigned long tpc = cregs->tpc; - int rval; - - if ((task_thread_info(child)->flags & _TIF_32BIT) != 0) - tpc &= 0xffffffff; - if (__put_user(cregs->tstate, (&pregs->tstate)) || - __put_user(tpc, (&pregs->tpc)) || - __put_user(cregs->tnpc, (&pregs->tnpc)) || - __put_user(cregs->y, (&pregs->y))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - for (rval = 1; rval < 16; rval++) - if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - pt_succ_return(regs, 0); -#ifdef DEBUG_PTRACE - printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]); -#endif - goto out_tsk; + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &fsr, + 32 * sizeof(u64), + 33 * sizeof(u64)); + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &gsr, + 33 * sizeof(u64), + 34 * sizeof(u64)); + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &fprs, + 34 * sizeof(u64), + 35 * sizeof(u64)); + + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 35 * sizeof(u64), -1); + + return ret; +} + +static int fpregs64_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *fpregs = task_thread_info(target)->fpregs; + unsigned long fprs; + int ret; + + if (target == current) + save_and_clear_fpu(); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u64)); + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + task_thread_info(target)->xfsr, + 32 * sizeof(u64), + 33 * sizeof(u64)); + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + task_thread_info(target)->gsr, + 33 * sizeof(u64), + 34 * sizeof(u64)); + + fprs = task_thread_info(target)->fpsaved[0]; + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fprs, + 34 * sizeof(u64), + 35 * sizeof(u64)); } - case PTRACE_SETREGS: { - struct pt_regs32 __user *pregs = - (struct pt_regs32 __user *) addr; - struct pt_regs *cregs = task_pt_regs(child); - unsigned int psr, pc, npc, y; - int i; - - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (__get_user(psr, (&pregs->psr)) || - __get_user(pc, (&pregs->pc)) || - __get_user(npc, (&pregs->npc)) || - __get_user(y, (&pregs->y))) { - pt_error_return(regs, EFAULT); - goto out_tsk; + fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU); + task_thread_info(target)->fpsaved[0] = fprs; + + if (!ret) + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 35 * sizeof(u64), -1); + return ret; +} + +static const struct user_regset sparc64_regsets[] = { + /* Format is: + * G0 --> G7 + * O0 --> O7 + * L0 --> L7 + * I0 --> I7 + * TSTATE, TPC, TNPC, Y + */ + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = 36 * sizeof(u64), + .size = sizeof(u64), .align = sizeof(u64), + .get = genregs64_get, .set = genregs64_set + }, + /* Format is: + * F0 --> F63 + * FSR + * GSR + * FPRS + */ + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, + .n = 35 * sizeof(u64), + .size = sizeof(u64), .align = sizeof(u64), + .get = fpregs64_get, .set = fpregs64_set + }, +}; + +static const struct user_regset_view user_sparc64_view = { + .name = "sparc64", .e_machine = EM_SPARCV9, + .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) +}; + +static int genregs32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct pt_regs *regs = task_pt_regs(target); + compat_ulong_t __user *reg_window; + compat_ulong_t *k = kbuf; + compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target == current) + flushw_user(); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) { + for (; count > 0 && pos < 16; count--) + *k++ = regs->u_regs[pos++]; + + reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(*k++, ®_window[pos++])) + return -EFAULT; } - cregs->tstate &= ~(TSTATE_ICC); - cregs->tstate |= psr_to_tstate_icc(psr); - if (!((pc | npc) & 3)) { - cregs->tpc = pc; - cregs->tnpc = npc; + } else { + for (; count > 0 && pos < 16; count--) { + if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) + return -EFAULT; } - cregs->y = y; - for (i = 1; i < 16; i++) { - if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } + + reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(reg, ®_window[pos++]) || + put_user(reg, u++)) + return -EFAULT; + } + } + while (count > 0) { + switch (pos) { + case 32: /* PSR */ + reg = tstate_to_psr(regs->tstate); + break; + case 33: /* PC */ + reg = regs->tpc; + break; + case 34: /* NPC */ + reg = regs->tnpc; + break; + case 35: /* Y */ + reg = regs->y; + break; + case 36: /* WIM */ + case 37: /* TBR */ + reg = 0; + break; + default: + goto finish; } - pt_succ_return(regs, 0); - goto out_tsk; + + if (kbuf) + *k++ = reg; + else if (put_user(reg, u++)) + return -EFAULT; + pos++; + count--; } +finish: + pos *= sizeof(reg); + count *= sizeof(reg); + + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 38 * sizeof(reg), -1); +} - case PTRACE_SETREGS64: { - struct pt_regs __user *pregs = (struct pt_regs __user *) addr; - struct pt_regs *cregs = task_pt_regs(child); - unsigned long tstate, tpc, tnpc, y; - int i; - - /* Must be careful, tracing process can only set certain - * bits in the psr. - */ - if (__get_user(tstate, (&pregs->tstate)) || - __get_user(tpc, (&pregs->tpc)) || - __get_user(tnpc, (&pregs->tnpc)) || - __get_user(y, (&pregs->y))) { - pt_error_return(regs, EFAULT); - goto out_tsk; +static int genregs32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct pt_regs *regs = task_pt_regs(target); + compat_ulong_t __user *reg_window; + const compat_ulong_t *k = kbuf; + const compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target == current) + flushw_user(); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) { + for (; count > 0 && pos < 16; count--) + regs->u_regs[pos++] = *k++; + + reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (put_user(*k++, ®_window[pos++])) + return -EFAULT; } - if ((task_thread_info(child)->flags & _TIF_32BIT) != 0) { - tpc &= 0xffffffff; - tnpc &= 0xffffffff; + } else { + for (; count > 0 && pos < 16; count--) { + if (get_user(reg, u++)) + return -EFAULT; + regs->u_regs[pos++] = reg; } - tstate &= (TSTATE_ICC | TSTATE_XCC); - cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); - cregs->tstate |= tstate; - if (!((tpc | tnpc) & 3)) { - cregs->tpc = tpc; - cregs->tnpc = tnpc; + + reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; + for (; count > 0 && pos < 32; count--) { + if (get_user(reg, u++) || + put_user(reg, ®_window[pos++])) + return -EFAULT; } - cregs->y = y; - for (i = 1; i < 16; i++) { - if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } + } + while (count > 0) { + unsigned long tstate; + + if (kbuf) + reg = *k++; + else if (get_user(reg, u++)) + return -EFAULT; + + switch (pos) { + case 32: /* PSR */ + tstate = regs->tstate; + tstate &= ~(TSTATE_ICC | TSTATE_XCC); + tstate |= psr_to_tstate_icc(reg); + regs->tstate = tstate; + break; + case 33: /* PC */ + regs->tpc = reg; + break; + case 34: /* NPC */ + regs->tnpc = reg; + break; + case 35: /* Y */ + regs->y = reg; + break; + case 36: /* WIM */ + case 37: /* TBR */ + break; + default: + goto finish; } - pt_succ_return(regs, 0); - goto out_tsk; + + pos++; + count--; } +finish: + pos *= sizeof(reg); + count *= sizeof(reg); - case PTRACE_GETFPREGS: { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - }; - struct fps __user *fps = (struct fps __user *) addr; - unsigned long *fpregs = task_thread_info(child)->fpregs; - - if (copy_to_user(&fps->regs[0], fpregs, - (32 * sizeof(unsigned int))) || - __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr)) || - __put_user(0, (&fps->fpqd)) || - __put_user(0, (&fps->flags)) || - __put_user(0, (&fps->extra)) || - clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - pt_succ_return(regs, 0); - goto out_tsk; + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 38 * sizeof(reg), -1); +} + +static int fpregs32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const unsigned long *fpregs = task_thread_info(target)->fpregs; + compat_ulong_t enabled; + unsigned long fprs; + compat_ulong_t fsr; + int ret = 0; + + if (target == current) + save_and_clear_fpu(); + + fprs = task_thread_info(target)->fpsaved[0]; + if (fprs & FPRS_FEF) { + fsr = task_thread_info(target)->xfsr[0]; + enabled = 1; + } else { + fsr = 0; + enabled = 0; } - case PTRACE_GETFPREGS64: { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - }; - struct fps __user *fps = (struct fps __user *) addr; - unsigned long *fpregs = task_thread_info(child)->fpregs; - - if (copy_to_user(&fps->regs[0], fpregs, - (64 * sizeof(unsigned int))) || - __put_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out_tsk; - } - pt_succ_return(regs, 0); - goto out_tsk; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &fsr, + 33 * sizeof(u32), + 34 * sizeof(u32)); + + if (!ret) { + compat_ulong_t val; + + val = (enabled << 8) | (8 << 16); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &val, + 34 * sizeof(u32), + 35 * sizeof(u32)); } - case PTRACE_SETFPREGS: { - struct fps { - unsigned int regs[32]; - unsigned int fsr; - unsigned int flags; - unsigned int extra; - unsigned int fpqd; - struct fq { - unsigned int insnaddr; - unsigned int insn; - } fpq[16]; - }; - struct fps __user *fps = (struct fps __user *) addr; - unsigned long *fpregs = task_thread_info(child)->fpregs; - unsigned fsr; - - if (copy_from_user(fpregs, &fps->regs[0], - (32 * sizeof(unsigned int))) || - __get_user(fsr, (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out_tsk; + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + 35 * sizeof(u32), -1); + + return ret; +} + +static int fpregs32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *fpregs = task_thread_info(target)->fpregs; + unsigned long fprs; + int ret; + + if (target == current) + save_and_clear_fpu(); + + fprs = task_thread_info(target)->fpsaved[0]; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, + 0, 32 * sizeof(u32)); + if (!ret) + user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 32 * sizeof(u32), + 33 * sizeof(u32)); + if (!ret && count > 0) { + compat_ulong_t fsr; + unsigned long val; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &fsr, + 33 * sizeof(u32), + 34 * sizeof(u32)); + if (!ret) { + val = task_thread_info(target)->xfsr[0]; + val &= 0xffffffff00000000UL; + val |= fsr; + task_thread_info(target)->xfsr[0] = val; } - task_thread_info(child)->xfsr[0] &= 0xffffffff00000000UL; - task_thread_info(child)->xfsr[0] |= fsr; - if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF)) - task_thread_info(child)->gsr[0] = 0; - task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL); - pt_succ_return(regs, 0); - goto out_tsk; } - case PTRACE_SETFPREGS64: { - struct fps { - unsigned int regs[64]; - unsigned long fsr; - }; - struct fps __user *fps = (struct fps __user *) addr; - unsigned long *fpregs = task_thread_info(child)->fpregs; - - if (copy_from_user(fpregs, &fps->regs[0], - (64 * sizeof(unsigned int))) || - __get_user(task_thread_info(child)->xfsr[0], (&fps->fsr))) { - pt_error_return(regs, EFAULT); - goto out_tsk; + fprs |= (FPRS_FEF | FPRS_DL); + task_thread_info(target)->fpsaved[0] = fprs; + + if (!ret) + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + 34 * sizeof(u32), -1); + return ret; +} + +static const struct user_regset sparc32_regsets[] = { + /* Format is: + * G0 --> G7 + * O0 --> O7 + * L0 --> L7 + * I0 --> I7 + * PSR, PC, nPC, Y, WIM, TBR + */ + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = 38 * sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .get = genregs32_get, .set = genregs32_set + }, + /* Format is: + * F0 --> F31 + * empty 32-bit word + * FSR (32--bit word) + * FPU QUEUE COUNT (8-bit char) + * FPU QUEUE ENTRYSIZE (8-bit char) + * FPU ENABLED (8-bit char) + * empty 8-bit char + * FPU QUEUE (64 32-bit ints) + */ + [REGSET_FP] = { + .core_note_type = NT_PRFPREG, + .n = 99 * sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .get = fpregs32_get, .set = fpregs32_set + }, +}; + +static const struct user_regset_view user_sparc32_view = { + .name = "sparc", .e_machine = EM_SPARC, + .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + if (test_tsk_thread_flag(task, TIF_32BIT)) + return &user_sparc32_view; + return &user_sparc64_view; +} + +struct compat_fps { + unsigned int regs[32]; + unsigned int fsr; + unsigned int flags; + unsigned int extra; + unsigned int fpqd; + struct compat_fq { + unsigned int insnaddr; + unsigned int insn; + } fpq[16]; +}; + +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) +{ + const struct user_regset_view *view = task_user_regset_view(child); + compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; + struct pt_regs32 __user *pregs; + struct compat_fps __user *fps; + unsigned long addr2 = caddr2; + unsigned long addr = caddr; + unsigned long data = cdata; + int ret; + + pregs = (struct pt_regs32 __user *) addr; + fps = (struct compat_fps __user *) addr; + + switch (request) { + case PTRACE_PEEKUSR: + ret = (addr != 0) ? -EIO : 0; + break; + + case PTRACE_GETREGS: + ret = copy_regset_to_user(child, view, REGSET_GENERAL, + 32 * sizeof(u32), + 4 * sizeof(u32), + &pregs->psr); + if (!ret) + ret = copy_regset_to_user(child, view, REGSET_GENERAL, + 1 * sizeof(u32), + 15 * sizeof(u32), + &pregs->u_regs[0]); + break; + + case PTRACE_SETREGS: + ret = copy_regset_from_user(child, view, REGSET_GENERAL, + 32 * sizeof(u32), + 4 * sizeof(u32), + &pregs->psr); + if (!ret) + ret = copy_regset_from_user(child, view, REGSET_GENERAL, + 1 * sizeof(u32), + 15 * sizeof(u32), + &pregs->u_regs[0]); + break; + + case PTRACE_GETFPREGS: + ret = copy_regset_to_user(child, view, REGSET_FP, + 0 * sizeof(u32), + 32 * sizeof(u32), + &fps->regs[0]); + if (!ret) + ret = copy_regset_to_user(child, view, REGSET_FP, + 33 * sizeof(u32), + 1 * sizeof(u32), + &fps->fsr); + if (!ret) { + if (__put_user(0, &fps->flags) || + __put_user(0, &fps->extra) || + __put_user(0, &fps->fpqd) || + clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) + ret = -EFAULT; } - if (!(task_thread_info(child)->fpsaved[0] & FPRS_FEF)) - task_thread_info(child)->gsr[0] = 0; - task_thread_info(child)->fpsaved[0] |= (FPRS_FEF | FPRS_DL | FPRS_DU); - pt_succ_return(regs, 0); - goto out_tsk; - } + break; + + case PTRACE_SETFPREGS: + ret = copy_regset_from_user(child, view, REGSET_FP, + 0 * sizeof(u32), + 32 * sizeof(u32), + &fps->regs[0]); + if (!ret) + ret = copy_regset_from_user(child, view, REGSET_FP, + 33 * sizeof(u32), + 1 * sizeof(u32), + &fps->fsr); + break; case PTRACE_READTEXT: - case PTRACE_READDATA: { - int res = ptrace_readdata(child, addr, - (char __user *)addr2, data); - if (res == data) { - pt_succ_return(regs, 0); - goto out_tsk; - } - if (res >= 0) - res = -EIO; - pt_error_return(regs, -res); - goto out_tsk; - } + case PTRACE_READDATA: + ret = ptrace_readdata(child, addr, + (char __user *)addr2, data); + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; + break; case PTRACE_WRITETEXT: - case PTRACE_WRITEDATA: { - int res = ptrace_writedata(child, (char __user *) addr2, - addr, data); - if (res == data) { - pt_succ_return(regs, 0); - goto out_tsk; - } - if (res >= 0) - res = -EIO; - pt_error_return(regs, -res); - goto out_tsk; + case PTRACE_WRITEDATA: + ret = ptrace_writedata(child, (char __user *) addr2, + addr, data); + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; + break; + + default: + ret = compat_ptrace_request(child, request, addr, data); + break; } - case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */ - addr = 1; - case PTRACE_CONT: { /* restart after signal. */ - if (!valid_signal(data)) { - pt_error_return(regs, EIO); - goto out_tsk; - } + return ret; +} - if (request == PTRACE_SYSCALL) { - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - } else { - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - } +struct fps { + unsigned int regs[64]; + unsigned long fsr; +}; - child->exit_code = data; -#ifdef DEBUG_PTRACE - printk("CONT: %s [%d]: set exit_code = %x %lx %lx\n", child->comm, - child->pid, child->exit_code, - task_pt_regs(child)->tpc, - task_pt_regs(child)->tnpc); - -#endif - wake_up_process(child); - pt_succ_return(regs, 0); - goto out_tsk; - } +long arch_ptrace(struct task_struct *child, long request, long addr, long data) +{ + const struct user_regset_view *view = task_user_regset_view(child); + struct pt_regs __user *pregs = (struct pt_regs __user *) addr; + unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; + struct fps __user *fps = (struct fps __user *) addr; + int ret; -/* - * make the child exit. Best I can do is send it a sigkill. - * perhaps it should be put in the status that it wants to - * exit. - */ - case PTRACE_KILL: { - if (child->exit_state == EXIT_ZOMBIE) { /* already dead */ - pt_succ_return(regs, 0); - goto out_tsk; + switch (request) { + case PTRACE_PEEKUSR: + ret = (addr != 0) ? -EIO : 0; + break; + + case PTRACE_GETREGS64: + ret = copy_regset_to_user(child, view, REGSET_GENERAL, + 1 * sizeof(u64), + 15 * sizeof(u64), + &pregs->u_regs[0]); + if (!ret) { + /* XXX doesn't handle 'y' register correctly XXX */ + ret = copy_regset_to_user(child, view, REGSET_GENERAL, + 32 * sizeof(u64), + 4 * sizeof(u64), + &pregs->tstate); } - child->exit_code = SIGKILL; - wake_up_process(child); - pt_succ_return(regs, 0); - goto out_tsk; - } + break; - case PTRACE_SUNDETACH: { /* detach a process that was attached. */ - int error = ptrace_detach(child, data); - if (error) { - pt_error_return(regs, EIO); - goto out_tsk; + case PTRACE_SETREGS64: + ret = copy_regset_from_user(child, view, REGSET_GENERAL, + 1 * sizeof(u64), + 15 * sizeof(u64), + &pregs->u_regs[0]); + if (!ret) { + /* XXX doesn't handle 'y' register correctly XXX */ + ret = copy_regset_from_user(child, view, REGSET_GENERAL, + 32 * sizeof(u64), + 4 * sizeof(u64), + &pregs->tstate); } - pt_succ_return(regs, 0); - goto out_tsk; - } + break; - /* PTRACE_DUMPCORE unsupported... */ + case PTRACE_GETFPREGS64: + ret = copy_regset_to_user(child, view, REGSET_FP, + 0 * sizeof(u64), + 33 * sizeof(u64), + fps); + break; - case PTRACE_GETEVENTMSG: { - int err; + case PTRACE_SETFPREGS64: + ret = copy_regset_to_user(child, view, REGSET_FP, + 0 * sizeof(u64), + 33 * sizeof(u64), + fps); + break; - if (test_thread_flag(TIF_32BIT)) - err = put_user(child->ptrace_message, - (unsigned int __user *) data); - else - err = put_user(child->ptrace_message, - (unsigned long __user *) data); - if (err) - pt_error_return(regs, -err); - else - pt_succ_return(regs, 0); + case PTRACE_READTEXT: + case PTRACE_READDATA: + ret = ptrace_readdata(child, addr, + (char __user *)addr2, data); + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; break; - } - default: { - int err = ptrace_request(child, request, addr, data); - if (err) - pt_error_return(regs, -err); - else - pt_succ_return(regs, 0); - goto out_tsk; - } + case PTRACE_WRITETEXT: + case PTRACE_WRITEDATA: + ret = ptrace_writedata(child, (char __user *) addr2, + addr, data); + if (ret == data) + ret = 0; + else if (ret >= 0) + ret = -EIO; + break; + + default: + ret = ptrace_request(child, request, addr, data); + break; } -out_tsk: - if (child) - put_task_struct(child); -out: - unlock_kernel(); + + return ret; } asmlinkage void syscall_trace(struct pt_regs *regs, int syscall_exit_p) diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index a813441b358..5964d8653ad 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <asm/smp.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/delay.h> #include <linux/fs.h> diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 8649635d6d7..68db0893039 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -53,7 +53,6 @@ #include <asm/ebus.h> #include <asm/isa.h> #endif -#include <asm/a.out.h> #include <asm/ns87303.h> #include <asm/timer.h> #include <asm/cpudata.h> diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index c56573a10ee..134d801579f 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -28,7 +28,6 @@ #include <asm/uaccess.h> #include <asm/utrap.h> #include <asm/perfctr.h> -#include <asm/a.out.h> #include <asm/unistd.h> /* #define DEBUG_UNIMP_SYSCALL */ diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 98c468803ce..deaba2bd053 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -57,7 +57,6 @@ #include <asm/fpumacro.h> #include <asm/semaphore.h> #include <asm/mmu_context.h> -#include <asm/a.out.h> #include <asm/compat_signal.h> asmlinkage long sys32_chown16(const char __user * filename, u16 user, u16 group) diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index adc62f490f3..6b9b718e24a 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -23,7 +23,7 @@ sys_call_table32: /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys32_chown16, sys32_mknod /*15*/ .word sys_chmod, sys32_lchown16, sparc_brk, sys32_perfctr, sys32_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys32_setuid16, sys32_getuid16 -/*25*/ .word sys32_vmsplice, sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause +/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice .word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile /*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 523e993ee90..e726c45645f 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -997,7 +997,7 @@ static unsigned long __init bootmem_init(unsigned long *pages_avail, prom_printf("reserve_bootmem(initrd): base[%llx] size[%lx]\n", initrd_start, initrd_end); #endif - reserve_bootmem(initrd_start, size); + reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT); initrd_start += PAGE_OFFSET; initrd_end += PAGE_OFFSET; @@ -1007,7 +1007,7 @@ static unsigned long __init bootmem_init(unsigned long *pages_avail, #ifdef CONFIG_DEBUG_BOOTMEM prom_printf("reserve_bootmem(kernel): base[%lx] size[%lx]\n", kern_base, kern_size); #endif - reserve_bootmem(kern_base, kern_size); + reserve_bootmem(kern_base, kern_size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(kern_size) >> PAGE_SHIFT; /* Add back in the initmem pages. */ @@ -1024,7 +1024,7 @@ static unsigned long __init bootmem_init(unsigned long *pages_avail, prom_printf("reserve_bootmem(bootmap): base[%lx] size[%lx]\n", (bootmap_pfn << PAGE_SHIFT), size); #endif - reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size); + reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size, BOOTMEM_DEFAULT); for (i = 0; i < pavail_ents; i++) { unsigned long start_pfn, end_pfn; @@ -1489,7 +1489,7 @@ static void __init taint_real_pages(void) goto do_next_page; } } - reserve_bootmem(old_start, PAGE_SIZE); + reserve_bootmem(old_start, PAGE_SIZE, BOOTMEM_DEFAULT); do_next_page: old_start += PAGE_SIZE; diff --git a/arch/sparc64/prom/init.c b/arch/sparc64/prom/init.c index 1c0db842a6f..87e7c7ea0ee 100644 --- a/arch/sparc64/prom/init.c +++ b/arch/sparc64/prom/init.c @@ -48,7 +48,10 @@ void __init prom_init(void *cif_handler, void *cif_stack) prom_getstring(node, "version", prom_version, sizeof(prom_version)); prom_printf("\n"); +} +void __init prom_init_report(void) +{ printk("PROMLIB: Sun IEEE Boot Prom '%s'\n", prom_version); printk("PROMLIB: Root node compatible: %s\n", prom_root_compatible); } diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index c86cb3091a8..d3e48e9701b 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -415,7 +415,7 @@ asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) switch (cmd) { case 0: /* getpgrp */ - return task_pgrp_nr(current); + return task_pgrp_vnr(current); case 1: /* setpgrp */ { int (*sys_setpgid)(pid_t,pid_t) = @@ -426,7 +426,7 @@ asmlinkage int solaris_procids(int cmd, s32 pid, s32 pgid) ret = sys_setpgid(0, 0); if (ret) return ret; proc_clear_tty(current); - return task_pgrp_nr(current); + return task_pgrp_vnr(current); } case 2: /* getsid */ { diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 99e51d059a0..dba8e05f028 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -203,17 +203,6 @@ config NR_CPUS depends on SMP default "32" -config NEST_LEVEL - int "Nesting level" - default "0" - help - This is set to the number of layers of UMLs that this UML will be run - in. Normally, this is zero, meaning that it will run directly on the - host. Setting it to one will build a UML that can run inside a UML - that is running on the host. Generally, if you intend this UML to run - inside another UML, set CONFIG_NEST_LEVEL to one more than the host - UML. - config HIGHMEM bool "Highmem support (EXPERIMENTAL)" depends on !64BIT && EXPERIMENTAL diff --git a/arch/um/Kconfig.i386 b/arch/um/Kconfig.i386 index 717f5d3440e..3cd8a04d66d 100644 --- a/arch/um/Kconfig.i386 +++ b/arch/um/Kconfig.i386 @@ -23,43 +23,6 @@ config SEMAPHORE_SLEEPERS bool default y -choice - prompt "Host memory split" - default HOST_VMSPLIT_3G - help - This is needed when the host kernel on which you run has a non-default - (like 2G/2G) memory split, instead of the customary 3G/1G. If you did - not recompile your own kernel but use the default distro's one, you can - safely accept the "Default split" option. - - It can be enabled on recent (>=2.6.16-rc2) vanilla kernels via - CONFIG_VM_SPLIT_*, or on previous kernels with special patches (-ck - patchset by Con Kolivas, or other ones) - option names match closely the - host CONFIG_VM_SPLIT_* ones. - - A lower setting (where 1G/3G is lowest and 3G/1G is higher) will - tolerate even more "normal" host kernels, but an higher setting will be - stricter. - - So, if you do not know what to do here, say 'Default split'. - -config HOST_VMSPLIT_3G - bool "Default split (3G/1G user/kernel host split)" -config HOST_VMSPLIT_3G_OPT - bool "3G/1G user/kernel host split (for full 1G low memory)" -config HOST_VMSPLIT_2G - bool "2G/2G user/kernel host split" -config HOST_VMSPLIT_1G - bool "1G/3G user/kernel host split" -endchoice - -config TOP_ADDR - hex - default 0xB0000000 if HOST_VMSPLIT_3G_OPT - default 0x78000000 if HOST_VMSPLIT_2G - default 0x40000000 if HOST_VMSPLIT_1G - default 0xC0000000 - config 3_LEVEL_PGTABLES bool "Three-level pagetables (EXPERIMENTAL)" default n @@ -84,3 +47,5 @@ config GENERIC_HWEIGHT bool default y +config ARCH_SUPPORTS_AOUT + def_bool y diff --git a/arch/um/Kconfig.x86_64 b/arch/um/Kconfig.x86_64 index d632e9a89cc..6533b349f06 100644 --- a/arch/um/Kconfig.x86_64 +++ b/arch/um/Kconfig.x86_64 @@ -15,10 +15,6 @@ config SEMAPHORE_SLEEPERS bool default y -config TOP_ADDR - hex - default 0x7fc0000000 - config 3_LEVEL_PGTABLES bool default y @@ -39,3 +35,5 @@ config GENERIC_HWEIGHT bool default y +config ARCH_SUPPORTS_AOUT + def_bool y diff --git a/arch/um/Makefile b/arch/um/Makefile index cb4af9bf207..dbeab15e7bb 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -79,13 +79,6 @@ KERNEL_DEFINES = $(strip -Derrno=kernel_errno -Dsigprocmask=kernel_sigprocmask \ KBUILD_CFLAGS += $(KERNEL_DEFINES) KBUILD_CFLAGS += $(call cc-option,-fno-unit-at-a-time,) -# These are needed for clean and mrproper, since in that case .config is not -# included; the values here are meaningless - -CONFIG_NEST_LEVEL ?= 0 - -SIZE = ($(CONFIG_NEST_LEVEL) * 0x20000000) - PHONY += linux all: linux @@ -120,10 +113,6 @@ CFLAGS_NO_HARDENING := $(call cc-option, -fno-PIC,) $(call cc-option, -fno-pic,) CONFIG_KERNEL_STACK_ORDER ?= 2 STACK_SIZE := $(shell echo $$[ 4096 * (1 << $(CONFIG_KERNEL_STACK_ORDER)) ] ) -ifndef START - START = $(shell echo $$[ $(TOP_ADDR) - $(SIZE) ] ) -endif - CPPFLAGS_vmlinux.lds = -U$(SUBARCH) -DSTART=$(START) -DELF_ARCH=$(ELF_ARCH) \ -DELF_FORMAT="$(ELF_FORMAT)" -DKERNEL_STACK_SIZE=$(STACK_SIZE) diff --git a/arch/um/defconfig b/arch/um/defconfig index 86db2862f22..59215bc264e 100644 --- a/arch/um/defconfig +++ b/arch/um/defconfig @@ -56,8 +56,6 @@ CONFIG_X86_TSC=y CONFIG_UML_X86=y # CONFIG_64BIT is not set CONFIG_SEMAPHORE_SLEEPERS=y -# CONFIG_HOST_2G_2G is not set -CONFIG_TOP_ADDR=0xc0000000 # CONFIG_3_LEVEL_PGTABLES is not set CONFIG_ARCH_HAS_SC_SIGNALS=y CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA=y @@ -81,7 +79,6 @@ CONFIG_HOSTFS=y # CONFIG_HPPFS is not set CONFIG_MCONSOLE=y CONFIG_MAGIC_SYSRQ=y -CONFIG_NEST_LEVEL=0 # CONFIG_HIGHMEM is not set CONFIG_KERNEL_STACK_ORDER=0 diff --git a/arch/um/include/as-layout.h b/arch/um/include/as-layout.h index 606bb5c7fdf..cac542d8ff7 100644 --- a/arch/um/include/as-layout.h +++ b/arch/um/include/as-layout.h @@ -57,6 +57,8 @@ extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end; extern unsigned long _unprotected_end; extern unsigned long brk_start; +extern unsigned long host_task_size; + extern int linux_main(int argc, char **argv); extern void (*sig_info[])(int, struct uml_pt_regs *); diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 0b6b6273330..32c799e3a49 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h @@ -295,6 +295,9 @@ extern void maybe_sigio_broken(int fd, int read); extern int os_arch_prctl(int pid, int code, unsigned long *addr); /* tty.c */ -int get_pty(void); +extern int get_pty(void); + +/* sys-$ARCH/task_size.c */ +extern unsigned long os_get_task_size(void); #endif diff --git a/arch/um/include/tempfile.h b/arch/um/include/tempfile.h deleted file mode 100644 index d441eac936b..00000000000 --- a/arch/um/include/tempfile.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __TEMPFILE_H__ -#define __TEMPFILE_H__ - -extern int make_tempfile(const char *template, char **tempname, int do_unlink); - -#endif diff --git a/arch/um/include/um_mmu.h b/arch/um/include/um_mmu.h index 82865fcf687..f575ff91f2a 100644 --- a/arch/um/include/um_mmu.h +++ b/arch/um/include/um_mmu.h @@ -13,6 +13,7 @@ typedef struct mm_context { struct mm_id id; struct uml_ldt ldt; + struct page **stub_pages; } mm_context_t; extern void __switch_mm(struct mm_id * mm_idp); diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c index 76a62c0cb2b..f5d7f4569ba 100644 --- a/arch/um/kernel/exec.c +++ b/arch/um/kernel/exec.c @@ -25,7 +25,7 @@ void flush_thread(void) ret = unmap(¤t->mm->context.id, 0, STUB_START, 0, &data); ret = ret || unmap(¤t->mm->context.id, STUB_END, - TASK_SIZE - STUB_END, 1, &data); + host_task_size - STUB_END, 1, &data); if (ret) { printk(KERN_ERR "flush_thread - clearing address space failed, " "err = %d\n", ret); diff --git a/arch/um/kernel/ksyms.c b/arch/um/kernel/ksyms.c index 5311ee93ede..66e2a305a8d 100644 --- a/arch/um/kernel/ksyms.c +++ b/arch/um/kernel/ksyms.c @@ -5,7 +5,6 @@ #include "linux/module.h" #include "linux/syscalls.h" -#include "asm/a.out.h" #include "asm/tlbflush.h" #include "asm/uaccess.h" #include "as-layout.h" @@ -60,7 +59,6 @@ EXPORT_SYMBOL(os_accept_connection); EXPORT_SYMBOL(os_rcv_fd); EXPORT_SYMBOL(run_helper); EXPORT_SYMBOL(start_thread); -EXPORT_SYMBOL(dump_thread); #ifdef CONFIG_SMP diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index d872fdce1d7..2627ce82e91 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -354,11 +354,13 @@ pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return pte; } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *pte; pte = alloc_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); + if (pte) + pgtable_page_ctor(pte); return pte; } diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index c07961bedb7..fc50d2f959d 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -258,10 +258,6 @@ void cpu_idle(void) default_idle(); } -void dump_thread(struct pt_regs *regs, struct user *u) -{ -} - int __cant_sleep(void) { return in_atomic() || irqs_disabled() || in_interrupt(); /* Is in_interrupt() really needed? */ diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 78b3e9f69d5..0cd9a7a05e7 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -91,6 +91,8 @@ int init_new_context(struct task_struct *task, struct mm_struct *mm) goto out_free; } + to_mm->stub_pages = NULL; + return 0; out_free: @@ -126,6 +128,7 @@ void arch_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm) pages[0] = virt_to_page(&__syscall_stub_start); pages[1] = virt_to_page(mm->context.id.stack); + mm->context.stub_pages = pages; /* dup_mmap already holds mmap_sem */ err = install_special_mapping(mm, STUB_START, STUB_END - STUB_START, @@ -147,6 +150,8 @@ void arch_exit_mmap(struct mm_struct *mm) { pte_t *pte; + if (mm->context.stub_pages != NULL) + kfree(mm->context.stub_pages); pte = virt_to_pte(mm, STUB_CODE); if (pte != NULL) pte_clear(mm, STUB_CODE, pte); diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 468aba990db..a6c1dd1cf5a 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -241,6 +241,11 @@ static struct notifier_block panic_exit_notifier = { }; /* Set during early boot */ +unsigned long task_size; +EXPORT_SYMBOL(task_size); + +unsigned long host_task_size; + unsigned long brk_start; unsigned long end_iomem; EXPORT_SYMBOL(end_iomem); @@ -267,6 +272,13 @@ int __init linux_main(int argc, char **argv) if (have_root == 0) add_arg(DEFAULT_COMMAND_LINE); + host_task_size = os_get_task_size(); + /* + * TASK_SIZE needs to be PGDIR_SIZE aligned or else exit_mmap craps + * out + */ + task_size = host_task_size & PGDIR_MASK; + /* OS sanity checks that need to happen before the kernel runs */ os_early_checks(); @@ -303,7 +315,7 @@ int __init linux_main(int argc, char **argv) highmem = 0; iomem_size = (iomem_size + PAGE_SIZE - 1) & PAGE_MASK; - max_physmem = CONFIG_TOP_ADDR - uml_physmem - iomem_size - MIN_VMALLOC; + max_physmem = TASK_SIZE - uml_physmem - iomem_size - MIN_VMALLOC; /* * Zones have to begin on a 1 << MAX_ORDER page boundary, @@ -335,7 +347,7 @@ int __init linux_main(int argc, char **argv) } virtmem_size = physmem_size; - avail = CONFIG_TOP_ADDR - start_vm; + avail = TASK_SIZE - start_vm; if (physmem_size > avail) virtmem_size = avail; end_vm = start_vm + virtmem_size; diff --git a/arch/um/os-Linux/aio.c b/arch/um/os-Linux/aio.c index b8d8c9ca8d4..57e3d46c989 100644 --- a/arch/um/os-Linux/aio.c +++ b/arch/um/os-Linux/aio.c @@ -142,7 +142,7 @@ static int do_not_aio(struct aio_thread_req *req) if (actual != req->offset) return -errno; - switch(req->type) { + switch (req->type) { case AIO_READ: n = read(req->io_fd, req->buf, req->len); break; diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c index 04f11b9f1ac..046a131f610 100644 --- a/arch/um/os-Linux/drivers/ethertap_kern.c +++ b/arch/um/os-Linux/drivers/ethertap_kern.c @@ -6,7 +6,7 @@ * Licensed under the GPL. */ -#include "linux/init.h" +#include <linux/init.h> #include <linux/netdevice.h> #include "etap.h" #include "net_kern.h" @@ -30,10 +30,10 @@ static void etap_init(struct net_device *dev, void *data) epri->control_fd = -1; epri->dev = dev; - printk("ethertap backend - %s", epri->dev_name); + printk(KERN_INFO "ethertap backend - %s", epri->dev_name); if (epri->gate_addr != NULL) - printk(", IP = %s", epri->gate_addr); - printk("\n"); + printk(KERN_CONT ", IP = %s", epri->gate_addr); + printk(KERN_CONT "\n"); } static int etap_read(int fd, struct sk_buff *skb, struct uml_net_private *lp) diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c index 9d384807b07..6b9e33d5de2 100644 --- a/arch/um/os-Linux/drivers/tuntap_kern.c +++ b/arch/um/os-Linux/drivers/tuntap_kern.c @@ -29,10 +29,10 @@ static void tuntap_init(struct net_device *dev, void *data) tpri->fd = -1; tpri->dev = dev; - printk("TUN/TAP backend - "); + printk(KERN_INFO "TUN/TAP backend - "); if (tpri->gate_addr != NULL) - printk("IP = %s", tpri->gate_addr); - printk("\n"); + printk(KERN_CONT "IP = %s", tpri->gate_addr); + printk(KERN_CONT "\n"); } static int tuntap_read(int fd, struct sk_buff *skb, struct uml_net_private *lp) diff --git a/arch/um/os-Linux/include/file.h b/arch/um/os-Linux/include/file.h index d82711efacf..fe71be24bd5 100644 --- a/arch/um/os-Linux/include/file.h +++ b/arch/um/os-Linux/include/file.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ @@ -9,14 +9,3 @@ #define DEV_NULL "/dev/null" #endif - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c index eedc2d88ef8..93a11d7edfa 100644 --- a/arch/um/os-Linux/mem.c +++ b/arch/um/os-Linux/mem.c @@ -1,22 +1,21 @@ +/* + * Copyright (C) 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Licensed under the GPL + */ + #include <stdio.h> -#include <stdlib.h> #include <stddef.h> -#include <stdarg.h> +#include <stdlib.h> #include <unistd.h> #include <errno.h> -#include <string.h> #include <fcntl.h> -#include <sys/types.h> +#include <string.h> #include <sys/mman.h> -#include <sys/statfs.h> -#include "user.h" -#include "mem_user.h" +#include <sys/param.h> #include "init.h" -#include "os.h" -#include "tempfile.h" #include "kern_constants.h" - -#include <sys/param.h> +#include "os.h" +#include "user.h" /* Modified by which_tmpdir, which is called during early boot */ static char *default_tmpdir = "/tmp"; @@ -33,18 +32,19 @@ static void __init find_tempdir(void) int i; char *dir = NULL; - if(tempdir != NULL) /* We've already been called */ + if (tempdir != NULL) + /* We've already been called */ return; - for(i = 0; dirs[i]; i++){ + for (i = 0; dirs[i]; i++) { dir = getenv(dirs[i]); - if((dir != NULL) && (*dir != '\0')) + if ((dir != NULL) && (*dir != '\0')) break; } - if((dir == NULL) || (*dir == '\0')) + if ((dir == NULL) || (*dir == '\0')) dir = default_tmpdir; tempdir = malloc(strlen(dir) + 2); - if(tempdir == NULL){ + if (tempdir == NULL) { fprintf(stderr, "Failed to malloc tempdir, " "errno = %d\n", errno); return; @@ -53,7 +53,8 @@ static void __init find_tempdir(void) strcat(tempdir, "/"); } -/* This will return 1, with the first character in buf being the +/* + * This will return 1, with the first character in buf being the * character following the next instance of c in the file. This will * read the file as needed. If there's an error, -errno is returned; * if the end of the file is reached, 0 is returned. @@ -64,11 +65,11 @@ static int next(int fd, char *buf, size_t size, char c) size_t len; char *ptr; - while((ptr = strchr(buf, c)) == NULL){ + while ((ptr = strchr(buf, c)) == NULL) { n = read(fd, buf, size - 1); - if(n == 0) + if (n == 0) return 0; - else if(n < 0) + else if (n < 0) return -errno; buf[n] = '\0'; @@ -78,11 +79,12 @@ static int next(int fd, char *buf, size_t size, char c) len = strlen(ptr); memmove(buf, ptr, len + 1); - /* Refill the buffer so that if there's a partial string that we care + /* + * Refill the buffer so that if there's a partial string that we care * about, it will be completed, and we can recognize it. */ n = read(fd, &buf[len], size - len - 1); - if(n < 0) + if (n < 0) return -errno; buf[len + n] = '\0'; @@ -92,7 +94,8 @@ static int next(int fd, char *buf, size_t size, char c) /* which_tmpdir is called only during early boot */ static int checked_tmpdir = 0; -/* Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner +/* + * Look for a tmpfs mounted at /dev/shm. I couldn't find a cleaner * way to do this than to parse /proc/mounts. statfs will return the * same filesystem magic number and fs id for both /dev and /dev/shm * when they are both tmpfs, so you can't tell if they are different @@ -107,7 +110,7 @@ static void which_tmpdir(void) int fd, found; char buf[128] = { '\0' }; - if(checked_tmpdir) + if (checked_tmpdir) return; checked_tmpdir = 1; @@ -115,28 +118,28 @@ static void which_tmpdir(void) printf("Checking for tmpfs mount on /dev/shm..."); fd = open("/proc/mounts", O_RDONLY); - if(fd < 0){ + if (fd < 0) { printf("failed to open /proc/mounts, errno = %d\n", errno); return; } - while(1){ + while (1) { found = next(fd, buf, ARRAY_SIZE(buf), ' '); - if(found != 1) + if (found != 1) break; - if(!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) + if (!strncmp(buf, "/dev/shm", strlen("/dev/shm"))) goto found; found = next(fd, buf, ARRAY_SIZE(buf), '\n'); - if(found != 1) + if (found != 1) break; } err: - if(found == 0) + if (found == 0) printf("nothing mounted on /dev/shm\n"); - else if(found < 0) + else if (found < 0) printf("read returned errno %d\n", -found); out: @@ -146,10 +149,10 @@ out: found: found = next(fd, buf, ARRAY_SIZE(buf), ' '); - if(found != 1) + if (found != 1) goto err; - if(strncmp(buf, "tmpfs", strlen("tmpfs"))){ + if (strncmp(buf, "tmpfs", strlen("tmpfs"))) { printf("not tmpfs\n"); goto out; } @@ -159,43 +162,40 @@ found: goto out; } -/* - * This proc still used in tt-mode - * (file: kernel/tt/ptproxy/proxy.c, proc: start_debugger). - * So it isn't 'static' yet. - */ -int __init make_tempfile(const char *template, char **out_tempname, - int do_unlink) +static int __init make_tempfile(const char *template, char **out_tempname, + int do_unlink) { char *tempname; int fd; which_tmpdir(); tempname = malloc(MAXPATHLEN); - if (!tempname) - goto out; + if (tempname == NULL) + return -1; find_tempdir(); + if ((tempdir == NULL) || (strlen(tempdir) >= MAXPATHLEN)) + return -1; + if (template[0] != '/') strcpy(tempname, tempdir); else tempname[0] = '\0'; strncat(tempname, template, MAXPATHLEN-1-strlen(tempname)); fd = mkstemp(tempname); - if(fd < 0){ + if (fd < 0) { fprintf(stderr, "open - cannot create %s: %s\n", tempname, strerror(errno)); goto out; } - if(do_unlink && (unlink(tempname) < 0)){ + if (do_unlink && (unlink(tempname) < 0)) { perror("unlink"); goto out; } - if(out_tempname){ + if (out_tempname) { *out_tempname = tempname; - } else { + } else free(tempname); - } return fd; out: free(tempname); @@ -204,27 +204,23 @@ out: #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" -/* - * This proc is used in start_up.c - * So it isn't 'static'. - */ -int __init create_tmp_file(unsigned long long len) +static int __init create_tmp_file(unsigned long long len) { int fd, err; char zero; fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); - if(fd < 0) { + if (fd < 0) exit(1); - } err = fchmod(fd, 0777); - if(err < 0){ + if (err < 0) { perror("fchmod"); exit(1); } - /* Seek to len - 1 because writing a character there will + /* + * Seek to len - 1 because writing a character there will * increase the file size by one byte, to the desired length. */ if (lseek64(fd, len - 1, SEEK_SET) < 0) { @@ -235,7 +231,7 @@ int __init create_tmp_file(unsigned long long len) zero = 0; err = write(fd, &zero, 1); - if(err != 1){ + if (err != 1) { perror("write"); exit(1); } @@ -250,7 +246,7 @@ int __init create_mem_file(unsigned long long len) fd = create_tmp_file(len); err = os_set_exec_close(fd); - if(err < 0){ + if (err < 0) { errno = -err; perror("exec_close"); } @@ -267,11 +263,11 @@ void __init check_tmpexec(void) PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); printf("Checking PROT_EXEC mmap in %s...",tempdir); fflush(stdout); - if(addr == MAP_FAILED){ + if (addr == MAP_FAILED) { err = errno; perror("failed"); close(fd); - if(err == EPERM) + if (err == EPERM) printf("%s must be not mounted noexec\n",tempdir); exit(1); } diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index abf6beae3df..e0477c3ee89 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -224,7 +224,7 @@ int __init can_drop_memory(void) goto out_unmap; } - printk("OK\n"); + printk(UM_KERN_CONT "OK\n"); ok = 1; out_unmap: diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 0fb0cc8d475..3f1694b134c 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c @@ -237,7 +237,7 @@ void unblock_signals(void) * interrupts may have arrived and we need to re-enable them and * recheck signals_pending. */ - while(1) { + while (1) { /* * Save and reset save_pending after enabling signals. This * way, signals_pending won't be changed while we're reading it. diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index d36c89c24a4..b14829469fa 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -341,7 +341,7 @@ void userspace(struct uml_pt_regs *regs) int local_using_sysemu; if (getitimer(ITIMER_VIRTUAL, &timer)) - printk("Failed to get itimer, errno = %d\n", errno); + printk(UM_KERN_ERR "Failed to get itimer, errno = %d\n", errno); nsecs = timer.it_value.tv_sec * UM_NSEC_PER_SEC + timer.it_value.tv_usec * UM_NSEC_PER_USEC; nsecs += os_nsecs(); @@ -388,7 +388,7 @@ void userspace(struct uml_pt_regs *regs) if (WIFSTOPPED(status)) { int sig = WSTOPSIG(status); - switch(sig) { + switch (sig) { case SIGSEGV: if (PTRACE_FULL_FAULTINFO || !ptrace_faultinfo) { @@ -641,7 +641,7 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf) * after returning to the jumper. */ n = setjmp(initial_jmpbuf); - switch(n) { + switch (n) { case INIT_JMP_NEW_THREAD: (*switch_buf)[0].JB_IP = (unsigned long) new_thread_handler; (*switch_buf)[0].JB_SP = (unsigned long) stack + diff --git a/arch/um/os-Linux/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile index a841262c594..b4bc6ac4f30 100644 --- a/arch/um/os-Linux/sys-i386/Makefile +++ b/arch/um/os-Linux/sys-i386/Makefile @@ -3,7 +3,7 @@ # Licensed under the GPL # -obj-y = registers.o signal.o tls.o +obj-y = registers.o signal.o task_size.o tls.o USER_OBJS := $(obj-y) diff --git a/arch/um/os-Linux/sys-i386/registers.c b/arch/um/os-Linux/sys-i386/registers.c index d1997ca76e5..f74d853a0ee 100644 --- a/arch/um/os-Linux/sys-i386/registers.c +++ b/arch/um/os-Linux/sys-i386/registers.c @@ -62,10 +62,10 @@ void arch_init_registers(int pid) int err; err = ptrace(PTRACE_GETFPXREGS, pid, 0, fpx_regs); - if(!err) + if (!err) return; - if(errno != EIO) + if (errno != EIO) panic("check_ptrace : PTRACE_GETFPXREGS failed, errno = %d", errno); diff --git a/arch/um/os-Linux/sys-i386/task_size.c b/arch/um/os-Linux/sys-i386/task_size.c new file mode 100644 index 00000000000..48d211b3d9a --- /dev/null +++ b/arch/um/os-Linux/sys-i386/task_size.c @@ -0,0 +1,120 @@ +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/mman.h> +#include "longjmp.h" +#include "kern_constants.h" + +static jmp_buf buf; + +static void segfault(int sig) +{ + longjmp(buf, 1); +} + +static int page_ok(unsigned long page) +{ + unsigned long *address = (unsigned long *) (page << UM_KERN_PAGE_SHIFT); + unsigned long n = ~0UL; + void *mapped = NULL; + int ok = 0; + + /* + * First see if the page is readable. If it is, it may still + * be a VDSO, so we go on to see if it's writable. If not + * then try mapping memory there. If that fails, then we're + * still in the kernel area. As a sanity check, we'll fail if + * the mmap succeeds, but gives us an address different from + * what we wanted. + */ + if (setjmp(buf) == 0) + n = *address; + else { + mapped = mmap(address, UM_KERN_PAGE_SIZE, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mapped == MAP_FAILED) + return 0; + if (mapped != address) + goto out; + } + + /* + * Now, is it writeable? If so, then we're in user address + * space. If not, then try mprotecting it and try the write + * again. + */ + if (setjmp(buf) == 0) { + *address = n; + ok = 1; + goto out; + } else if (mprotect(address, UM_KERN_PAGE_SIZE, + PROT_READ | PROT_WRITE) != 0) + goto out; + + if (setjmp(buf) == 0) { + *address = n; + ok = 1; + } + + out: + if (mapped != NULL) + munmap(mapped, UM_KERN_PAGE_SIZE); + return ok; +} + +unsigned long os_get_task_size(void) +{ + struct sigaction sa, old; + unsigned long bottom = 0; + /* + * A 32-bit UML on a 64-bit host gets confused about the VDSO at + * 0xffffe000. It is mapped, is readable, can be reprotected writeable + * and written. However, exec discovers later that it can't be + * unmapped. So, just set the highest address to be checked to just + * below it. This might waste some address space on 4G/4G 32-bit + * hosts, but shouldn't hurt otherwise. + */ + unsigned long top = 0xffffd000 >> UM_KERN_PAGE_SHIFT; + unsigned long test; + + printf("Locating the top of the address space ... "); + fflush(stdout); + + /* + * We're going to be longjmping out of the signal handler, so + * SA_DEFER needs to be set. + */ + sa.sa_handler = segfault; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + sigaction(SIGSEGV, &sa, &old); + + if (!page_ok(bottom)) { + fprintf(stderr, "Address 0x%x no good?\n", + bottom << UM_KERN_PAGE_SHIFT); + exit(1); + } + + /* This could happen with a 4G/4G split */ + if (page_ok(top)) + goto out; + + do { + test = bottom + (top - bottom) / 2; + if (page_ok(test)) + bottom = test; + else + top = test; + } while (top - bottom > 1); + +out: + /* Restore the old SIGSEGV handling */ + sigaction(SIGSEGV, &old, NULL); + + top <<= UM_KERN_PAGE_SHIFT; + printf("0x%x\n", top); + fflush(stdout); + + return top; +} diff --git a/arch/um/os-Linux/sys-x86_64/Makefile b/arch/um/os-Linux/sys-x86_64/Makefile index a42a4ef02e1..a44a47f8f57 100644 --- a/arch/um/os-Linux/sys-x86_64/Makefile +++ b/arch/um/os-Linux/sys-x86_64/Makefile @@ -3,7 +3,7 @@ # Licensed under the GPL # -obj-y = registers.o prctl.o signal.o +obj-y = registers.o prctl.o signal.o task_size.o USER_OBJS := $(obj-y) diff --git a/arch/um/os-Linux/sys-x86_64/registers.c b/arch/um/os-Linux/sys-x86_64/registers.c index 9bfa789992d..a375853337a 100644 --- a/arch/um/os-Linux/sys-x86_64/registers.c +++ b/arch/um/os-Linux/sys-x86_64/registers.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) + * Copyright (C) 2006 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ @@ -7,31 +7,36 @@ #include <sys/ptrace.h> #define __FRAME_OFFSETS #include <asm/ptrace.h> +#include "kern_constants.h" #include "longjmp.h" #include "user.h" int save_fp_registers(int pid, unsigned long *fp_regs) { - if(ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_GETFPREGS, pid, 0, fp_regs) < 0) return -errno; return 0; } int restore_fp_registers(int pid, unsigned long *fp_regs) { - if(ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) + if (ptrace(PTRACE_SETFPREGS, pid, 0, fp_regs) < 0) return -errno; return 0; } unsigned long get_thread_reg(int reg, jmp_buf *buf) { - switch(reg){ - case RIP: return buf[0]->__rip; - case RSP: return buf[0]->__rsp; - case RBP: return buf[0]->__rbp; + switch (reg) { + case RIP: + return buf[0]->__rip; + case RSP: + return buf[0]->__rsp; + case RBP: + return buf[0]->__rbp; default: - printk("get_thread_regs - unknown register %d\n", reg); + printk(UM_KERN_ERR "get_thread_regs - unknown register %d\n", + reg); return 0; } } diff --git a/arch/um/os-Linux/sys-x86_64/task_size.c b/arch/um/os-Linux/sys-x86_64/task_size.c new file mode 100644 index 00000000000..fad6f57f8ee --- /dev/null +++ b/arch/um/os-Linux/sys-x86_64/task_size.c @@ -0,0 +1,5 @@ +unsigned long os_get_task_size(unsigned long shift) +{ + /* The old value of CONFIG_TOP_ADDR */ + return 0x7fc0000000; +} diff --git a/arch/um/os-Linux/uaccess.c b/arch/um/os-Linux/uaccess.c index 8d27b6d1df9..087ed74ffca 100644 --- a/arch/um/os-Linux/uaccess.c +++ b/arch/um/os-Linux/uaccess.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) - * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ @@ -16,7 +16,7 @@ unsigned long __do_user_copy(void *to, const void *from, int n, jmp_buf jbuf; *fault_catcher = &jbuf; - if(UML_SETJMP(&jbuf) == 0){ + if (UML_SETJMP(&jbuf) == 0) { (*op)(to, from, n); ret = 0; *faulted_out = 0; diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c index 4c37b1b1d0b..74f49bb9b12 100644 --- a/arch/um/os-Linux/user_syms.c +++ b/arch/um/os-Linux/user_syms.c @@ -34,8 +34,8 @@ EXPORT_SYMBOL(printf); * good; so the versions of these symbols will always match */ #define EXPORT_SYMBOL_PROTO(sym) \ - int sym(void); \ - EXPORT_SYMBOL(sym); + int sym(void); \ + EXPORT_SYMBOL(sym); extern void readdir64(void) __attribute__((weak)); EXPORT_SYMBOL(readdir64); diff --git a/arch/um/os-Linux/util.c b/arch/um/os-Linux/util.c index a6f31d47699..6ea77979531 100644 --- a/arch/um/os-Linux/util.c +++ b/arch/um/os-Linux/util.c @@ -1,39 +1,24 @@ /* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) * Licensed under the GPL */ #include <stdio.h> #include <stdlib.h> -#include <unistd.h> -#include <limits.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/utsname.h> -#include <sys/param.h> -#include <sys/time.h> -#include "asm/types.h" -#include <ctype.h> -#include <signal.h> -#include <wait.h> #include <errno.h> -#include <stdarg.h> -#include <sched.h> -#include <termios.h> +#include <signal.h> #include <string.h> -#include "kern_util.h" -#include "user.h" -#include "mem_user.h" -#include "init.h" -#include "ptrace_user.h" -#include "uml-config.h" -#include "os.h" -#include "longjmp.h" +#include <termios.h> +#include <wait.h> +#include <sys/mman.h> +#include <sys/utsname.h> #include "kern_constants.h" +#include "os.h" +#include "user.h" void stack_protections(unsigned long address) { - if(mprotect((void *) address, UM_THREAD_SIZE, + if (mprotect((void *) address, UM_THREAD_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) panic("protecting stack failed, errno = %d", errno); } @@ -44,17 +29,19 @@ int raw(int fd) int err; CATCH_EINTR(err = tcgetattr(fd, &tt)); - if(err < 0) + if (err < 0) return -errno; cfmakeraw(&tt); CATCH_EINTR(err = tcsetattr(fd, TCSADRAIN, &tt)); - if(err < 0) + if (err < 0) return -errno; - /* XXX tcsetattr could have applied only some changes - * (and cfmakeraw() is a set of changes) */ + /* + * XXX tcsetattr could have applied only some changes + * (and cfmakeraw() is a set of changes) + */ return 0; } diff --git a/arch/v850/Kconfig b/arch/v850/Kconfig index ace479ab273..7b6d3716efc 100644 --- a/arch/v850/Kconfig +++ b/arch/v850/Kconfig @@ -53,6 +53,9 @@ config ARCH_HAS_ILOG2_U64 bool default n +config ARCH_SUPPORTS_AOUT + def_bool y + # Turn off some random 386 crap that can affect device config config ISA bool @@ -212,6 +215,13 @@ menu "Processor type and features" bool default !V850E_CACHE && !V850E2_CACHE + # HZ depends on the platform + config HZ + int + default 24 if V850E_SIM || V850E2_SIM85E2 + default 122 if V850E2_FPGA85E2C + default 100 + #### Misc config config ROM_KERNEL diff --git a/arch/v850/kernel/anna.c b/arch/v850/kernel/anna.c index 0e429041a11..5978a25170f 100644 --- a/arch/v850/kernel/anna.c +++ b/arch/v850/kernel/anna.c @@ -85,7 +85,8 @@ void __init mach_reserve_bootmem () /* The space between SRAM and SDRAM is filled with duplicate images of SRAM. Prevent the kernel from using them. */ reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE)); + SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), + BOOTMEM_DEFAULT); } void mach_gettimeofday (struct timespec *tv) diff --git a/arch/v850/kernel/as85ep1.c b/arch/v850/kernel/as85ep1.c index 18437bc5c3a..b525ecf3aea 100644 --- a/arch/v850/kernel/as85ep1.c +++ b/arch/v850/kernel/as85ep1.c @@ -116,7 +116,8 @@ void __init mach_reserve_bootmem () if (SDRAM_ADDR < RAM_END && SDRAM_ADDR > RAM_START) /* We can't use the space between SRAM and SDRAM, so prevent the kernel from trying. */ - reserve_bootmem (SRAM_END, SDRAM_ADDR - SRAM_END); + reserve_bootmem(SRAM_END, SDRAM_ADDR - SRAM_END, + BOOTMEM_DEFAULT); } void mach_gettimeofday (struct timespec *tv) diff --git a/arch/v850/kernel/procfs.c b/arch/v850/kernel/procfs.c index e6f9d060ad5..e433cde789b 100644 --- a/arch/v850/kernel/procfs.c +++ b/arch/v850/kernel/procfs.c @@ -59,7 +59,7 @@ static void cpuinfo_stop (struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = cpuinfo_start, .next = cpuinfo_next, .stop = cpuinfo_stop, diff --git a/arch/v850/kernel/rte_ma1_cb.c b/arch/v850/kernel/rte_ma1_cb.c index 9a716f94642..08abf3d5f8d 100644 --- a/arch/v850/kernel/rte_ma1_cb.c +++ b/arch/v850/kernel/rte_ma1_cb.c @@ -46,13 +46,15 @@ void __init mach_reserve_bootmem () { #ifdef CONFIG_RTE_CB_MULTI /* Prevent the kernel from touching the monitor's scratch RAM. */ - reserve_bootmem (MON_SCRATCH_ADDR, MON_SCRATCH_SIZE); + reserve_bootmem(MON_SCRATCH_ADDR, MON_SCRATCH_SIZE, + BOOTMEM_DEFAULT); #endif /* The space between SRAM and SDRAM is filled with duplicate images of SRAM. Prevent the kernel from using them. */ reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE)); + SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), + BOOTMEM_DEFAULT); } void mach_gettimeofday (struct timespec *tv) diff --git a/arch/v850/kernel/setup.c b/arch/v850/kernel/setup.c index a914f244f49..a0a8456a843 100644 --- a/arch/v850/kernel/setup.c +++ b/arch/v850/kernel/setup.c @@ -241,15 +241,18 @@ init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len) if (kram_end > kram_start) /* Reserve the RAM part of the kernel's address space, so it doesn't get allocated. */ - reserve_bootmem (kram_start, kram_end - kram_start); + reserve_bootmem(kram_start, kram_end - kram_start, + BOOTMEM_DEFAULT); if (intv_in_ram && !intv_in_kram) /* Reserve the interrupt vector space. */ - reserve_bootmem (intv_start, intv_end - intv_start); + reserve_bootmem(intv_start, intv_end - intv_start, + BOOTMEM_DEFAULT); if (bootmap >= ram_start && bootmap < ram_end) /* Reserve the bootmap space. */ - reserve_bootmem (bootmap, bootmap_len); + reserve_bootmem(bootmap, bootmap_len, + BOOTMEM_DEFAULT); /* Reserve the memory used by the root filesystem image if it's in RAM. */ @@ -257,7 +260,8 @@ init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len) && (unsigned long)&_root_fs_image_start >= ram_start && (unsigned long)&_root_fs_image_start < ram_end) reserve_bootmem ((unsigned long)&_root_fs_image_start, - &_root_fs_image_end - &_root_fs_image_start); + &_root_fs_image_end - &_root_fs_image_start, + BOOTMEM_DEFAULT); /* Let the platform-dependent code reserve some too. */ if (mrb) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e6728bd61cc..65a70b777c1 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -52,6 +52,10 @@ config HAVE_LATENCYTOP_SUPPORT config SEMAPHORE_SLEEPERS def_bool y +config FAST_CMPXCHG_LOCAL + bool + default y + config MMU def_bool y @@ -105,6 +109,9 @@ config GENERIC_TIME_VSYSCALL bool default X86_64 +config ARCH_HAS_CPU_RELAX + def_bool y + config HAVE_SETUP_PER_CPU_AREA def_bool X86_64 @@ -129,6 +136,9 @@ config AUDIT_ARCH bool default X86_64 +config ARCH_SUPPORTS_AOUT + def_bool y + # Use the generic interrupt handling code in kernel/irq/: config GENERIC_HARDIRQS bool @@ -631,7 +641,6 @@ config TOSHIBA config I8K tristate "Dell laptop support" - depends on X86_32 ---help--- This adds a driver to safely access the System Management Mode of the CPU on the Dell Inspiron 8000. The System Management Mode @@ -1571,7 +1580,7 @@ config IA32_EMULATION config IA32_AOUT tristate "IA32 a.out support" - depends on IA32_EMULATION + depends on IA32_EMULATION && ARCH_SUPPORTS_AOUT help Support old a.out binaries in the 32bit emulation. diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c index e4c12079171..58cccb6483b 100644 --- a/arch/x86/ia32/ia32_aout.c +++ b/arch/x86/ia32/ia32_aout.c @@ -172,8 +172,7 @@ static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, has_dumped = 1; current->flags |= PF_DUMPCORE; strncpy(dump.u_comm, current->comm, sizeof(current->comm)); - dump.u_ar0 = (u32)(((unsigned long)(&dump.regs)) - - ((unsigned long)(&dump))); + dump.u_ar0 = offsetof(struct user32, regs); dump.signal = signr; dump_thread32(regs, &dump); diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index d2a58431a07..680b7300a48 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -78,7 +78,6 @@ int acpi_ht __initdata = 1; /* enable HT */ int acpi_lapic; int acpi_ioapic; int acpi_strict; -EXPORT_SYMBOL(acpi_strict); u8 acpi_sci_flags __initdata; int acpi_sci_override_gsi __initdata; @@ -106,7 +105,7 @@ enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PIC; #ifdef CONFIG_X86_64 /* rely on all ACPI tables being in the direct mapping */ -char *__acpi_map_table(unsigned long phys_addr, unsigned long size) +char *__init __acpi_map_table(unsigned long phys_addr, unsigned long size) { if (!phys_addr || !size) return NULL; @@ -131,7 +130,7 @@ char *__acpi_map_table(unsigned long phys_addr, unsigned long size) * from the fixed base. That's why we start at FIX_IO_APIC_BASE_END and * count idx down while incrementing the phys address. */ -char *__acpi_map_table(unsigned long phys, unsigned long size) +char *__init __acpi_map_table(unsigned long phys, unsigned long size) { unsigned long base, offset, mapped_size; int idx; @@ -490,8 +489,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity) return irq; } -EXPORT_SYMBOL(acpi_register_gsi); - /* * ACPI based hotplug support for CPU */ @@ -587,25 +584,6 @@ int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base) EXPORT_SYMBOL(acpi_unregister_ioapic); -static unsigned long __init -acpi_scan_rsdp(unsigned long start, unsigned long length) -{ - unsigned long offset = 0; - unsigned long sig_len = sizeof("RSD PTR ") - 1; - - /* - * Scan all 16-byte boundaries of the physical memory region for the - * RSDP signature. - */ - for (offset = 0; offset < length; offset += 16) { - if (strncmp((char *)(phys_to_virt(start) + offset), "RSD PTR ", sig_len)) - continue; - return (start + offset); - } - - return 0; -} - static int __init acpi_parse_sbf(struct acpi_table_header *table) { struct acpi_table_boot *sb; @@ -748,27 +726,6 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table) return 0; } -unsigned long __init acpi_find_rsdp(void) -{ - unsigned long rsdp_phys = 0; - - if (efi_enabled) { - if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) - return efi.acpi20; - else if (efi.acpi != EFI_INVALID_TABLE_ADDR) - return efi.acpi; - } - /* - * Scan memory looking for the RSDP signature. First search EBDA (low - * memory) paragraphs and then search upper memory (E0000-FFFFF). - */ - rsdp_phys = acpi_scan_rsdp(0, 0x400); - if (!rsdp_phys) - rsdp_phys = acpi_scan_rsdp(0xE0000, 0x20000); - - return rsdp_phys; -} - #ifdef CONFIG_X86_LOCAL_APIC /* * Parse LAPIC entries in MADT diff --git a/arch/x86/kernel/acpi/processor.c b/arch/x86/kernel/acpi/processor.c index a25db514c71..324eb0cab19 100644 --- a/arch/x86/kernel/acpi/processor.c +++ b/arch/x86/kernel/acpi/processor.c @@ -46,6 +46,12 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c) buf[1] = 1; buf[2] = ACPI_PDC_C_CAPABILITY_SMP; + /* + * The default of PDC_SMP_T_SWCOORD bit is set for intel x86 cpu so + * that OSPM is capable of native ACPI throttling software + * coordination using BIOS supplied _TSD info. + */ + buf[2] |= ACPI_PDC_SMP_T_SWCOORD; if (cpu_has(c, X86_FEATURE_EST)) buf[2] |= ACPI_PDC_EST_CAPABILITY_SWSMP; diff --git a/arch/x86/kernel/cpu/cpufreq/Kconfig b/arch/x86/kernel/cpu/cpufreq/Kconfig index 151eda0a23f..cb7a5715596 100644 --- a/arch/x86/kernel/cpu/cpufreq/Kconfig +++ b/arch/x86/kernel/cpu/cpufreq/Kconfig @@ -29,7 +29,7 @@ config X86_ACPI_CPUFREQ config ELAN_CPUFREQ tristate "AMD Elan SC400 and SC410" select CPU_FREQ_TABLE - depends on X86_32 && X86_ELAN + depends on X86_ELAN ---help--- This adds the CPUFreq driver for AMD Elan SC400 and SC410 processors. @@ -45,7 +45,7 @@ config ELAN_CPUFREQ config SC520_CPUFREQ tristate "AMD Elan SC520" select CPU_FREQ_TABLE - depends on X86_32 && X86_ELAN + depends on X86_ELAN ---help--- This adds the CPUFreq driver for AMD Elan SC520 processor. diff --git a/arch/x86/kernel/cpu/cpufreq/e_powersaver.c b/arch/x86/kernel/cpu/cpufreq/e_powersaver.c index 326a4c81f68..39f8cb18296 100644 --- a/arch/x86/kernel/cpu/cpufreq/e_powersaver.c +++ b/arch/x86/kernel/cpu/cpufreq/e_powersaver.c @@ -23,6 +23,7 @@ #define EPS_BRAND_C7 1 #define EPS_BRAND_EDEN 2 #define EPS_BRAND_C3 3 +#define EPS_BRAND_C7D 4 struct eps_cpu_data { u32 fsb; @@ -54,6 +55,7 @@ static int eps_set_state(struct eps_cpu_data *centaur, { struct cpufreq_freqs freqs; u32 lo, hi; + u8 current_multiplier, current_voltage; int err = 0; int i; @@ -93,6 +95,15 @@ postchange: rdmsr(MSR_IA32_PERF_STATUS, lo, hi); freqs.new = centaur->fsb * ((lo >> 8) & 0xff); + /* Print voltage and multiplier */ + rdmsr(MSR_IA32_PERF_STATUS, lo, hi); + current_voltage = lo & 0xff; + printk(KERN_INFO "eps: Current voltage = %dmV\n", + current_voltage * 16 + 700); + current_multiplier = (lo >> 8) & 0xff; + printk(KERN_INFO "eps: Current multiplier = %d\n", + current_multiplier); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); return err; } @@ -141,9 +152,10 @@ static int eps_cpu_init(struct cpufreq_policy *policy) u8 current_multiplier, current_voltage; u8 max_multiplier, max_voltage; u8 min_multiplier, min_voltage; - u8 brand; + u8 brand = 0; u32 fsb; struct eps_cpu_data *centaur; + struct cpuinfo_x86 *c = &cpu_data(0); struct cpufreq_frequency_table *f_table; int k, step, voltage; int ret; @@ -153,21 +165,36 @@ static int eps_cpu_init(struct cpufreq_policy *policy) return -ENODEV; /* Check brand */ - printk("eps: Detected VIA "); - rdmsr(0x1153, lo, hi); - brand = (((lo >> 2) ^ lo) >> 18) & 3; + printk(KERN_INFO "eps: Detected VIA "); + + switch (c->x86_model) { + case 10: + rdmsr(0x1153, lo, hi); + brand = (((lo >> 2) ^ lo) >> 18) & 3; + printk(KERN_CONT "Model A "); + break; + case 13: + rdmsr(0x1154, lo, hi); + brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff; + printk(KERN_CONT "Model D "); + break; + } + switch(brand) { case EPS_BRAND_C7M: - printk("C7-M\n"); + printk(KERN_CONT "C7-M\n"); break; case EPS_BRAND_C7: - printk("C7\n"); + printk(KERN_CONT "C7\n"); break; case EPS_BRAND_EDEN: - printk("Eden\n"); + printk(KERN_CONT "Eden\n"); + break; + case EPS_BRAND_C7D: + printk(KERN_CONT "C7-D\n"); break; case EPS_BRAND_C3: - printk("C3\n"); + printk(KERN_CONT "C3\n"); return -ENODEV; break; } @@ -179,7 +206,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) /* Can be locked at 0 */ rdmsrl(MSR_IA32_MISC_ENABLE, val); if (!(val & 1 << 16)) { - printk("eps: Can't enable Enhanced PowerSaver\n"); + printk(KERN_INFO "eps: Can't enable Enhanced PowerSaver\n"); return -ENODEV; } } @@ -187,19 +214,19 @@ static int eps_cpu_init(struct cpufreq_policy *policy) /* Print voltage and multiplier */ rdmsr(MSR_IA32_PERF_STATUS, lo, hi); current_voltage = lo & 0xff; - printk("eps: Current voltage = %dmV\n", current_voltage * 16 + 700); + printk(KERN_INFO "eps: Current voltage = %dmV\n", current_voltage * 16 + 700); current_multiplier = (lo >> 8) & 0xff; - printk("eps: Current multiplier = %d\n", current_multiplier); + printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier); /* Print limits */ max_voltage = hi & 0xff; - printk("eps: Highest voltage = %dmV\n", max_voltage * 16 + 700); + printk(KERN_INFO "eps: Highest voltage = %dmV\n", max_voltage * 16 + 700); max_multiplier = (hi >> 8) & 0xff; - printk("eps: Highest multiplier = %d\n", max_multiplier); + printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier); min_voltage = (hi >> 16) & 0xff; - printk("eps: Lowest voltage = %dmV\n", min_voltage * 16 + 700); + printk(KERN_INFO "eps: Lowest voltage = %dmV\n", min_voltage * 16 + 700); min_multiplier = (hi >> 24) & 0xff; - printk("eps: Lowest multiplier = %d\n", min_multiplier); + printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier); /* Sanity checks */ if (current_multiplier == 0 || max_multiplier == 0 @@ -208,7 +235,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) if (current_multiplier > max_multiplier || max_multiplier <= min_multiplier) return -EINVAL; - if (current_voltage > 0x1c || max_voltage > 0x1c) + if (current_voltage > 0x1f || max_voltage > 0x1f) return -EINVAL; if (max_voltage < min_voltage) return -EINVAL; @@ -310,7 +337,7 @@ static int __init eps_init(void) /* This driver will work only on Centaur C7 processors with * Enhanced SpeedStep/PowerSaver registers */ if (c->x86_vendor != X86_VENDOR_CENTAUR - || c->x86 != 6 || c->x86_model != 10) + || c->x86 != 6 || c->x86_model < 10) return -ENODEV; if (!cpu_has(c, X86_FEATURE_EST)) return -ENODEV; diff --git a/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c b/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c index 2ed7db2fd25..9d9eae82e60 100644 --- a/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c +++ b/arch/x86/kernel/cpu/cpufreq/gx-suspmod.c @@ -181,8 +181,8 @@ static __init struct pci_dev *gx_detect_chipset(void) struct pci_dev *gx_pci = NULL; /* check if CPU is a MediaGX or a Geode. */ - if ((current_cpu_data.x86_vendor != X86_VENDOR_NSC) && - (current_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) { + if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) && + (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) { dprintk("error: no MediaGX/Geode processor found!\n"); return NULL; } diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k7.c b/arch/x86/kernel/cpu/cpufreq/powernow-k7.c index b5a9863d6cd..0a61159d7b7 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k7.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k7.c @@ -460,7 +460,7 @@ static int powernow_decode_bios (int maxfid, int startvid) latency = psb->settlingtime; if (latency < 100) { - printk (KERN_INFO PFX "BIOS set settling time to %d microseconds." + printk(KERN_INFO PFX "BIOS set settling time to %d microseconds. " "Should be at least 100. Correcting.\n", latency); latency = 100; } diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index 5affe91ca1e..c99d59d8ef2 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c @@ -578,10 +578,9 @@ static void print_basics(struct powernow_k8_data *data) for (j = 0; j < data->numps; j++) { if (data->powernow_table[j].frequency != CPUFREQ_ENTRY_INVALID) { if (cpu_family == CPU_HW_PSTATE) { - printk(KERN_INFO PFX " %d : fid 0x%x did 0x%x (%d MHz)\n", + printk(KERN_INFO PFX " %d : pstate %d (%d MHz)\n", j, - (data->powernow_table[j].index & 0xff00) >> 8, - (data->powernow_table[j].index & 0xff0000) >> 16, + data->powernow_table[j].index, data->powernow_table[j].frequency/1000); } else { printk(KERN_INFO PFX " %d : fid 0x%x (%d MHz), vid 0x%x\n", @@ -1235,8 +1234,10 @@ static unsigned int powernowk8_get (unsigned int cpu) struct powernow_k8_data *data; cpumask_t oldmask = current->cpus_allowed; unsigned int khz = 0; + unsigned int first; - data = per_cpu(powernow_data, first_cpu(per_cpu(cpu_core_map, cpu))); + first = first_cpu(per_cpu(cpu_core_map, cpu)); + data = per_cpu(powernow_data, first); if (!data) return -EINVAL; diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h index afd2b520d35..ab48cfed4d9 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.h +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.h @@ -47,7 +47,7 @@ struct powernow_k8_data { #define CPUID_XFAM 0x0ff00000 /* extended family */ #define CPUID_XFAM_K8 0 #define CPUID_XMOD 0x000f0000 /* extended model */ -#define CPUID_XMOD_REV_MASK 0x00080000 +#define CPUID_XMOD_REV_MASK 0x000c0000 #define CPUID_XFAM_10H 0x00100000 /* family 0x10 */ #define CPUID_USE_XFAM_XMOD 0x00000f00 #define CPUID_GET_MAX_CAPABILITIES 0x80000000 diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c index 76c3ab0da46..98d4fdb7dc0 100644 --- a/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c +++ b/arch/x86/kernel/cpu/cpufreq/speedstep-lib.c @@ -189,10 +189,7 @@ static unsigned int pentium4_get_frequency(void) printk(KERN_DEBUG "speedstep-lib: couldn't detect FSB speed. Please send an e-mail to <linux@brodo.de>\n"); /* Multiplier. */ - if (c->x86_model < 2) - mult = msr_lo >> 27; - else - mult = msr_lo >> 24; + mult = msr_lo >> 24; dprintk("P4 - FSB %u kHz; Multiplier %u; Speed %u kHz\n", fsb, mult, (fsb * mult)); diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index c1cfd60639d..d0b234c9fc3 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -151,7 +151,7 @@ NORET_TYPE void machine_kexec(struct kimage *image) void arch_crash_save_vmcoreinfo(void) { -#ifdef CONFIG_ARCH_DISCONTIGMEM_ENABLE +#ifdef CONFIG_NUMA VMCOREINFO_SYMBOL(node_data); VMCOREINFO_LENGTH(node_data, MAX_NUMNODES); #endif diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index a1fef42f8cd..236d2f8f7dd 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -234,5 +234,10 @@ NORET_TYPE void machine_kexec(struct kimage *image) void arch_crash_save_vmcoreinfo(void) { VMCOREINFO_SYMBOL(init_level4_pgt); + +#ifdef CONFIG_NUMA + VMCOREINFO_SYMBOL(node_data); + VMCOREINFO_LENGTH(node_data, MAX_NUMNODES); +#endif } diff --git a/arch/x86/kernel/mpparse_32.c b/arch/x86/kernel/mpparse_32.c index 67009cdd5ec..f349e68e45a 100644 --- a/arch/x86/kernel/mpparse_32.c +++ b/arch/x86/kernel/mpparse_32.c @@ -736,7 +736,8 @@ static int __init smp_scan_config (unsigned long base, unsigned long length) smp_found_config = 1; printk(KERN_INFO "found SMP MP-table at [%p] %08lx\n", mpf, virt_to_phys(mpf)); - reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE); + reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE, + BOOTMEM_DEFAULT); if (mpf->mpf_physptr) { /* * We cannot access to MPC table to compute @@ -751,7 +752,8 @@ static int __init smp_scan_config (unsigned long base, unsigned long length) unsigned long end = max_low_pfn * PAGE_SIZE; if (mpf->mpf_physptr + size > end) size = end - mpf->mpf_physptr; - reserve_bootmem(mpf->mpf_physptr, size); + reserve_bootmem(mpf->mpf_physptr, size, + BOOTMEM_DEFAULT); } mpf_found = mpf; diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index dabdbeff1f7..a7d50a547dc 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -23,7 +23,6 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/utsname.h> #include <linux/delay.h> @@ -539,55 +538,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, return err; } -/* - * fill in the user structure for a core dump.. - */ -void dump_thread(struct pt_regs * regs, struct user * dump) -{ - u16 gs; - -/* changed the size calculations - should hopefully work better. lbt */ - dump->magic = CMAGIC; - dump->start_code = 0; - dump->start_stack = regs->sp & ~(PAGE_SIZE - 1); - dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; - dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT; - dump->u_dsize -= dump->u_tsize; - dump->u_ssize = 0; - dump->u_debugreg[0] = current->thread.debugreg0; - dump->u_debugreg[1] = current->thread.debugreg1; - dump->u_debugreg[2] = current->thread.debugreg2; - dump->u_debugreg[3] = current->thread.debugreg3; - dump->u_debugreg[4] = 0; - dump->u_debugreg[5] = 0; - dump->u_debugreg[6] = current->thread.debugreg6; - dump->u_debugreg[7] = current->thread.debugreg7; - - if (dump->start_stack < TASK_SIZE) - dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; - - dump->regs.bx = regs->bx; - dump->regs.cx = regs->cx; - dump->regs.dx = regs->dx; - dump->regs.si = regs->si; - dump->regs.di = regs->di; - dump->regs.bp = regs->bp; - dump->regs.ax = regs->ax; - dump->regs.ds = (u16)regs->ds; - dump->regs.es = (u16)regs->es; - dump->regs.fs = (u16)regs->fs; - savesegment(gs,gs); - dump->regs.orig_ax = regs->orig_ax; - dump->regs.ip = regs->ip; - dump->regs.cs = (u16)regs->cs; - dump->regs.flags = regs->flags; - dump->regs.sp = regs->sp; - dump->regs.ss = (u16)regs->ss; - - dump->u_fpvalid = dump_fpu (regs, &dump->i387); -} -EXPORT_SYMBOL(dump_thread); - #ifdef CONFIG_SECCOMP static void hard_disable_TSC(void) { diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 137a86171c3..b0cc8f0136d 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -26,7 +26,6 @@ #include <linux/smp.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/utsname.h> #include <linux/delay.h> diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c index 62adc5f20be..d1d8c347cc0 100644 --- a/arch/x86/kernel/setup_32.c +++ b/arch/x86/kernel/setup_32.c @@ -390,7 +390,7 @@ static void __init reserve_ebda_region(void) unsigned int addr; addr = get_bios_ebda(); if (addr) - reserve_bootmem(addr, PAGE_SIZE); + reserve_bootmem(addr, PAGE_SIZE, BOOTMEM_DEFAULT); } #ifndef CONFIG_NEED_MULTIPLE_NODES @@ -484,7 +484,8 @@ static void __init reserve_crashkernel(void) (unsigned long)(total_mem >> 20)); crashk_res.start = crash_base; crashk_res.end = crash_base + crash_size - 1; - reserve_bootmem(crash_base, crash_size); + reserve_bootmem(crash_base, crash_size, + BOOTMEM_DEFAULT); } else printk(KERN_INFO "crashkernel reservation failed - " "you have to specify a base address\n"); @@ -525,7 +526,7 @@ static void __init reserve_initrd(void) } if (ramdisk_end <= end_of_lowmem) { /* All in lowmem, easy case */ - reserve_bootmem(ramdisk_image, ramdisk_size); + reserve_bootmem(ramdisk_image, ramdisk_size, BOOTMEM_DEFAULT); initrd_start = ramdisk_image + PAGE_OFFSET; initrd_end = initrd_start+ramdisk_size; return; @@ -536,7 +537,7 @@ static void __init reserve_initrd(void) /* Note: this includes all the lowmem currently occupied by the initrd, we rely on that fact to keep the data intact. */ - reserve_bootmem(ramdisk_here, ramdisk_size); + reserve_bootmem(ramdisk_here, ramdisk_size, BOOTMEM_DEFAULT); initrd_start = ramdisk_here + PAGE_OFFSET; initrd_end = initrd_start + ramdisk_size; @@ -606,13 +607,14 @@ void __init setup_bootmem_allocator(void) * bootmem allocator with an invalid RAM area. */ reserve_bootmem(__pa_symbol(_text), (PFN_PHYS(min_low_pfn) + - bootmap_size + PAGE_SIZE-1) - __pa_symbol(_text)); + bootmap_size + PAGE_SIZE-1) - __pa_symbol(_text), + BOOTMEM_DEFAULT); /* * reserve physical page 0 - it's a special BIOS page on many boxes, * enabling clean reboots, SMP operation, laptop functions. */ - reserve_bootmem(0, PAGE_SIZE); + reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT); /* reserve EBDA region, it's a 4K region */ reserve_ebda_region(); @@ -622,7 +624,7 @@ void __init setup_bootmem_allocator(void) unless you have no PS/2 mouse plugged in. */ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && boot_cpu_data.x86 == 6) - reserve_bootmem(0xa0000 - 4096, 4096); + reserve_bootmem(0xa0000 - 4096, 4096, BOOTMEM_DEFAULT); #ifdef CONFIG_SMP /* @@ -630,7 +632,7 @@ void __init setup_bootmem_allocator(void) * FIXME: Don't need the extra page at 4K, but need to fix * trampoline before removing it. (see the GDT stuff) */ - reserve_bootmem(PAGE_SIZE, PAGE_SIZE); + reserve_bootmem(PAGE_SIZE, PAGE_SIZE, BOOTMEM_DEFAULT); #endif #ifdef CONFIG_ACPI_SLEEP /* diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c index c8939dfddfb..c0d8208af12 100644 --- a/arch/x86/kernel/setup_64.c +++ b/arch/x86/kernel/setup_64.c @@ -15,7 +15,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/screen_info.h> #include <linux/ioport.h> #include <linux/delay.h> @@ -189,7 +188,7 @@ contig_initmem_init(unsigned long start_pfn, unsigned long end_pfn) bootmap_size = init_bootmem(bootmap >> PAGE_SHIFT, end_pfn); e820_register_active_regions(0, start_pfn, end_pfn); free_bootmem_with_active_regions(0, end_pfn); - reserve_bootmem(bootmap, bootmap_size); + reserve_bootmem(bootmap, bootmap_size, BOOTMEM_DEFAULT); } #endif @@ -220,28 +219,35 @@ static inline void copy_edd(void) #ifdef CONFIG_KEXEC static void __init reserve_crashkernel(void) { - unsigned long long free_mem; + unsigned long long total_mem; unsigned long long crash_size, crash_base; int ret; - free_mem = - ((unsigned long long)max_low_pfn - min_low_pfn) << PAGE_SHIFT; + total_mem = ((unsigned long long)max_low_pfn - min_low_pfn) << PAGE_SHIFT; - ret = parse_crashkernel(boot_command_line, free_mem, + ret = parse_crashkernel(boot_command_line, total_mem, &crash_size, &crash_base); if (ret == 0 && crash_size) { - if (crash_base > 0) { - printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " - "for crashkernel (System RAM: %ldMB)\n", - (unsigned long)(crash_size >> 20), - (unsigned long)(crash_base >> 20), - (unsigned long)(free_mem >> 20)); - crashk_res.start = crash_base; - crashk_res.end = crash_base + crash_size - 1; - reserve_bootmem(crash_base, crash_size); - } else + if (crash_base <= 0) { printk(KERN_INFO "crashkernel reservation failed - " "you have to specify a base address\n"); + return; + } + + if (reserve_bootmem(crash_base, crash_size, + BOOTMEM_EXCLUSIVE) < 0) { + printk(KERN_INFO "crashkernel reservation failed - " + "memory is in use\n"); + return; + } + + printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " + "for crashkernel (System RAM: %ldMB)\n", + (unsigned long)(crash_size >> 20), + (unsigned long)(crash_base >> 20), + (unsigned long)(total_mem >> 20)); + crashk_res.start = crash_base; + crashk_res.end = crash_base + crash_size - 1; } } #else diff --git a/arch/x86/kernel/srat_32.c b/arch/x86/kernel/srat_32.c index 2bf6903cb44..b72e61359c3 100644 --- a/arch/x86/kernel/srat_32.c +++ b/arch/x86/kernel/srat_32.c @@ -274,7 +274,7 @@ int __init get_memcfg_from_srat(void) int tables = 0; int i = 0; - rsdp_address = acpi_find_rsdp(); + rsdp_address = acpi_os_get_root_pointer(); if (!rsdp_address) { printk("%s: System description tables not found\n", __FUNCTION__); diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index 0380795121a..c737849e2ef 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -77,7 +77,7 @@ unsigned long __init native_calculate_cpu_khz(void) reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i); } local_irq_save(flags); - /* start meauring cycles, incrementing from 0 */ + /* start measuring cycles, incrementing from 0 */ wrmsrl(MSR_K7_PERFCTR0 + i, 0); wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76); rdtscl(tsc_start); diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c index 04b1d20e261..c394ca0720b 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/discontig_32.c @@ -391,7 +391,8 @@ unsigned long __init setup_memory(void) void __init numa_kva_reserve(void) { if (kva_pages) - reserve_bootmem(PFN_PHYS(kva_start_pfn), PFN_PHYS(kva_pages)); + reserve_bootmem(PFN_PHYS(kva_start_pfn), PFN_PHYS(kva_pages), + BOOTMEM_DEFAULT); } void __init zone_sizes_init(void) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 9b61c75a235..5fe880fc305 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -644,9 +644,9 @@ void __init reserve_bootmem_generic(unsigned long phys, unsigned len) /* Should check here against the e820 map to avoid double free */ #ifdef CONFIG_NUMA - reserve_bootmem_node(NODE_DATA(nid), phys, len); + reserve_bootmem_node(NODE_DATA(nid), phys, len, BOOTMEM_DEFAULT); #else - reserve_bootmem(phys, len); + reserve_bootmem(phys, len, BOOTMEM_DEFAULT); #endif if (phys+len <= MAX_DMA_PFN*PAGE_SIZE) { dma_reserve += len / PAGE_SIZE; diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index 5a02bf4c91e..1aecc658cd7 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c @@ -238,9 +238,10 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, free_bootmem_with_active_regions(nodeid, end); - reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size); + reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size, + BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(nodeid), bootmap_start, - bootmap_pages<<PAGE_SHIFT); + bootmap_pages<<PAGE_SHIFT, BOOTMEM_DEFAULT); #ifdef CONFIG_ACPI_NUMA srat_reserve_add_area(nodeid); #endif diff --git a/arch/x86/mm/pgtable_32.c b/arch/x86/mm/pgtable_32.c index 6c1914622a8..73aba712520 100644 --- a/arch/x86/mm/pgtable_32.c +++ b/arch/x86/mm/pgtable_32.c @@ -183,7 +183,7 @@ pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) return (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO); } -struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) +pgtable_t pte_alloc_one(struct mm_struct *mm, unsigned long address) { struct page *pte; @@ -192,6 +192,8 @@ struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) #else pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0); #endif + if (pte) + pgtable_page_ctor(pte); return pte; } @@ -365,6 +367,7 @@ void check_pgt_cache(void) void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte) { + pgtable_page_dtor(pte); paravirt_release_pt(page_to_pfn(pte)); tlb_remove_page(tlb, pte); } diff --git a/arch/x86/mm/srat_64.c b/arch/x86/mm/srat_64.c index 65416f843e5..ecd91ea8a8a 100644 --- a/arch/x86/mm/srat_64.c +++ b/arch/x86/mm/srat_64.c @@ -488,7 +488,8 @@ void __init srat_reserve_add_area(int nodeid) printk(KERN_INFO "SRAT: This will cost you %Lu MB of " "pre-allocated memory.\n", (unsigned long long)total_mb); reserve_bootmem_node(NODE_DATA(nodeid), nodes_add[nodeid].start, - nodes_add[nodeid].end - nodes_add[nodeid].start); + nodes_add[nodeid].end - nodes_add[nodeid].start, + BOOTMEM_DEFAULT); } } diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 5d5546ce88f..fd36764d7fb 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -49,6 +49,10 @@ config ARCH_HAS_ILOG2_U64 config NO_IOPORT def_bool y +config HZ + int + default 100 + source "init/Kconfig" menu "Processor type and features" diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 2e8d398cf19..b80f2cb1b4f 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -469,7 +469,7 @@ c_stop(struct seq_file *f, void *v) { } -struct seq_operations cpuinfo_op = +const struct seq_operations cpuinfo_op = { start: c_start, next: c_next, |