From 9a4af027a03e10e97f56081cd7dd1fda5282bd9c Mon Sep 17 00:00:00 2001 From: Andre Draszik Date: Mon, 24 Aug 2009 14:38:27 +0900 Subject: sh: ratelimit unaligned fixups This patch makes sure we see messages about unaligned access fixups every now and then. Else especially userspace apps suffering from bad programming won't ever be noticed... Signed-off by: Andre Draszik Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/kernel/traps_32.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 05a04b6df844..866c7c7a8236 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -359,13 +359,6 @@ static inline int handle_delayslot(struct pt_regs *regs, #define SH_PC_8BIT_OFFSET(instr) ((((signed char)(instr))*2) + 4) #define SH_PC_12BIT_OFFSET(instr) ((((signed short)(instr<<4))>>3) + 4) -/* - * XXX: SH-2A needs this too, but it needs an overhaul thanks to mixed 32-bit - * opcodes.. - */ - -static int handle_unaligned_notify_count = 10; - int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, struct mem_access *ma) { @@ -375,15 +368,13 @@ int handle_unaligned_access(insn_size_t instruction, struct pt_regs *regs, index = (instruction>>8)&15; /* 0x0F00 */ rm = regs->regs[index]; - /* shout about the first ten userspace fixups */ - if (user_mode(regs) && handle_unaligned_notify_count>0) { - handle_unaligned_notify_count--; - - printk(KERN_NOTICE "Fixing up unaligned userspace access " + /* shout about fixups */ + if (printk_ratelimit()) + printk(KERN_NOTICE "Fixing up unaligned %s access " "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", + user_mode(regs) ? "userspace" : "kernel", current->comm, task_pid_nr(current), (void *)regs->pc, instruction); - } ret = -EFAULT; switch (instruction&0xF000) { -- cgit v1.2.3 From 7436cde6b2ca71049051620c68c26522bb3403bf Mon Sep 17 00:00:00 2001 From: Andre Draszik Date: Mon, 24 Aug 2009 14:53:46 +0900 Subject: sh: Allow user control over misaligned fixup handling This patch brings the SH4 misaligned trap handler in line with what happens on ARM: Add a /proc/cpu/alignment which can be read from to get alignment trap statistics and written to to influence the behaviour of the alignment trap handling. The value to write is a bitfield, which has the following meaning: 1 warn, 2 fixup, 4 signal In addition, we add a /proc/cpu/kernel_alignment, to enable or disable warnings in case of kernel code causing alignment errors. Signed-off by: Andre Draszik Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/kernel/traps_32.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 866c7c7a8236..0ad356d00ac8 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,87 @@ #define TRAP_ILLEGAL_SLOT_INST 13 #endif +static unsigned long se_user; +static unsigned long se_sys; +static unsigned long se_skipped; +static unsigned long se_half; +static unsigned long se_word; +static unsigned long se_dword; +static unsigned long se_multi; +/* bitfield: 1: warn 2: fixup 4: signal -> combinations 2|4 && 1|2|4 are not + valid! */ +static int se_usermode = 3; +/* 0: no warning 1: print a warning message */ +static int se_kernmode_warn = 1; + +#ifdef CONFIG_PROC_FS +static const char *se_usermode_action[] = { + "ignored", + "warn", + "fixup", + "fixup+warn", + "signal", + "signal+warn" +}; + +static int +proc_alignment_read(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + char *p = page; + int len; + + p += sprintf(p, "User:\t\t%lu\n", se_user); + p += sprintf(p, "System:\t\t%lu\n", se_sys); + p += sprintf(p, "Skipped:\t%lu\n", se_skipped); + p += sprintf(p, "Half:\t\t%lu\n", se_half); + p += sprintf(p, "Word:\t\t%lu\n", se_word); + p += sprintf(p, "DWord:\t\t%lu\n", se_dword); + p += sprintf(p, "Multi:\t\t%lu\n", se_multi); + p += sprintf(p, "User faults:\t%i (%s)\n", se_usermode, + se_usermode_action[se_usermode]); + p += sprintf(p, "Kernel faults:\t%i (fixup%s)\n", se_kernmode_warn, + se_kernmode_warn ? "+warn" : ""); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int proc_alignment_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char mode; + + if (count > 0) { + if (get_user(mode, buffer)) + return -EFAULT; + if (mode >= '0' && mode <= '5') + se_usermode = mode - '0'; + } + return count; +} + +static int proc_alignment_kern_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char mode; + + if (count > 0) { + if (get_user(mode, buffer)) + return -EFAULT; + if (mode >= '0' && mode <= '1') + se_kernmode_warn = mode - '0'; + } + return count; +} +#endif + static void dump_mem(const char *str, unsigned long bottom, unsigned long top) { unsigned long p; @@ -194,6 +276,13 @@ static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs, count = 1<<(instruction&3); + switch (count) { + case 1: se_half += 1; break; + case 2: se_word += 1; break; + case 4: se_dword += 1; break; + case 8: se_multi += 1; break; /* ??? */ + } + ret = -EFAULT; switch (instruction>>12) { case 0: /* mov.[bwl] to/from memory via r0+rn */ @@ -530,6 +619,27 @@ asmlinkage void do_address_error(struct pt_regs *regs, local_irq_enable(); + se_user += 1; + + /* shout about userspace fixups */ + if (se_usermode & 1) + printk(KERN_NOTICE "Unaligned userspace access " + "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", + current->comm, current->pid, (void *)regs->pc, + instruction); + + if (se_usermode & 2) + goto fixup; + + if (se_usermode & 4) + goto uspace_segv; + else { + /* ignore */ + trace_mark(kernel_arch_trap_exit, MARK_NOARGS); + return; + } + +fixup: /* bad PC is not something we can fix */ if (regs->pc & 1) { si_code = BUS_ADRALN; @@ -563,6 +673,14 @@ uspace_segv: info.si_addr = (void __user *)address; force_sig_info(SIGBUS, &info, current); } else { + se_sys += 1; + + if (se_kernmode_warn) + printk(KERN_NOTICE "Unaligned kernel access " + "on behalf of \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", + current->comm, current->pid, (void *)regs->pc, + instruction); + if (regs->pc & 1) die("unaligned program counter", regs, error_code); @@ -872,3 +990,38 @@ void dump_stack(void) show_stack(NULL, NULL); } EXPORT_SYMBOL(dump_stack); + +#ifdef CONFIG_PROC_FS +/* + * This needs to be done after sysctl_init, otherwise sys/ will be + * overwritten. Actually, this shouldn't be in sys/ at all since + * it isn't a sysctl, and it doesn't contain sysctl information. + * We now locate it in /proc/cpu/alignment instead. + */ +static int __init alignment_init(void) +{ + struct proc_dir_entry *dir, *res; + + dir = proc_mkdir("cpu", NULL); + if (!dir) + return -ENOMEM; + + res = create_proc_entry("alignment", S_IWUSR | S_IRUGO, dir); + if (!res) + return -ENOMEM; + + res->read_proc = proc_alignment_read; + res->write_proc = proc_alignment_write; + + res = create_proc_entry("kernel_alignment", S_IWUSR | S_IRUGO, dir); + if (!res) + return -ENOMEM; + + res->read_proc = proc_alignment_read; + res->write_proc = proc_alignment_kern_write; + + return 0; +} + +fs_initcall(alignment_init); +#endif -- cgit v1.2.3 From 5a0ab35e43a6e3c69893c0091fe6a78ea8b3e443 Mon Sep 17 00:00:00 2001 From: Andre Draszik Date: Mon, 24 Aug 2009 15:01:10 +0900 Subject: sh: cleanup of do_address_error() This patch fixes a few problems with the existing code in do_address_error(). a) the variable used to printk()d the offending instruction wasn't initialized correctly. This is a fix to bug 5727 b) behaviour for CONFIG_CPU_SH2A wasn't correct c) the 'ignore address error' behaviour didn't update the PC, causing an infinite loop. Signed-off-by: Andre Draszik Signed-off-by: Paul Mundt --- arch/sh/kernel/traps_32.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/arch/sh/kernel/traps_32.c b/arch/sh/kernel/traps_32.c index 0ad356d00ac8..c581dc31d92a 100644 --- a/arch/sh/kernel/traps_32.c +++ b/arch/sh/kernel/traps_32.c @@ -621,12 +621,21 @@ asmlinkage void do_address_error(struct pt_regs *regs, se_user += 1; +#ifndef CONFIG_CPU_SH2A + set_fs(USER_DS); + if (copy_from_user(&instruction, (u16 *)(regs->pc & ~1), 2)) { + set_fs(oldfs); + goto uspace_segv; + } + set_fs(oldfs); + /* shout about userspace fixups */ if (se_usermode & 1) printk(KERN_NOTICE "Unaligned userspace access " "in \"%s\" pid=%d pc=0x%p ins=0x%04hx\n", current->comm, current->pid, (void *)regs->pc, instruction); +#endif if (se_usermode & 2) goto fixup; @@ -635,7 +644,7 @@ asmlinkage void do_address_error(struct pt_regs *regs, goto uspace_segv; else { /* ignore */ - trace_mark(kernel_arch_trap_exit, MARK_NOARGS); + regs->pc += instruction_size(instruction); return; } @@ -647,15 +656,6 @@ fixup: } set_fs(USER_DS); - if (copy_from_user(&instruction, (void __user *)(regs->pc), - sizeof(instruction))) { - /* Argh. Fault on the instruction itself. - This should never happen non-SMP - */ - set_fs(oldfs); - goto uspace_segv; - } - tmp = handle_unaligned_access(instruction, regs, &user_mem_access); set_fs(oldfs); -- cgit v1.2.3 From 2fc742f8d64c68b4a175a1dcb28351b112d63315 Mon Sep 17 00:00:00 2001 From: Carl Shaw Date: Mon, 24 Aug 2009 15:07:08 +0900 Subject: sh: Improve unwind info for signals GCC does not issue unwind information for function epilogues. Unfortunately we can catch a signal during an epilogue. The signal handler writes the current context and signal return code onto the stack overwriting previous contents. During unwinding, libgcc can try to restore registers from the stack and restores corrupted ones. This can lead to segmentation, misaligned access and sigbus faults. For example, consider the following code: mov.l r12,@-r15 mov.l r14,@-r15 sts.l pr,@-r15 mov r15,r14 mov r14, r15 lds.l @r15+, pr <<< SIGNAL HERE mov.l @r15+, r14 mov.l @r15+, r12 rts Unwind is aware that pr was pushed to stack in prolog, so tries to restore it. Unfortunately it restores the last word of the signal handler code placed on the stack by the kernel. This patch tries to avoid the problem by adding a guard region on the stack between where the function pushes data and where the signal handler pushes its return code. We probably don't see this problem often because exception handling unwinding in an epilogue only occurs due to a pthread cancel signal. Also the kernel signal stack handler alignment of 8 bytes could hide the occurance of this problem sometimes as the stack may not be trampled at a particular required word. This is not guaranteed to always work. It relies on a frame pointer existing for the function (so it can get the correct sp value) which is not always the case for the SH4. Modifications will also be made to libgcc for the case where there is no fp. Signed-off-by: Carl Shaw Signed-off-by: Paul Mundt --- arch/sh/kernel/signal_32.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c index b5afbec1db59..6010750c90b4 100644 --- a/arch/sh/kernel/signal_32.c +++ b/arch/sh/kernel/signal_32.c @@ -40,6 +40,16 @@ struct fdpic_func_descriptor { unsigned long GOT; }; +/* + * The following define adds a 64 byte gap between the signal + * stack frame and previous contents of the stack. This allows + * frame unwinding in a function epilogue but only if a frame + * pointer is used in the function. This is necessary because + * current gcc compilers (<4.3) do not generate unwind info on + * SH for function epilogues. + */ +#define UNWINDGUARD 64 + /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -327,7 +337,7 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size) sp = current->sas_ss_sp + current->sas_ss_size; } - return (void __user *)((sp - frame_size) & -8ul); + return (void __user *)((sp - (frame_size+UNWINDGUARD)) & -8ul); } /* These symbols are defined with the addresses in the vsyscall page. -- cgit v1.2.3 From 15444a8973dcfbd286b3e638cbadac2446a9271a Mon Sep 17 00:00:00 2001 From: David McKay Date: Mon, 24 Aug 2009 16:10:40 +0900 Subject: sh: Allow use of GENERIC_IOMAP The synopsys PCI cell used in the later STMicro chips requires code to be run in order to do IO cycles, rather than just memory mapping the IO space. Rather than extending the existing SH infrastructure to allow this, use the GENERIC_IOMAP implmentation to save re-inventing the wheel. This set of changes allows the SH to be built with GENERIC_IOMAP enabled, it just ifdef's out the functions provided by the GENERIC_IOMAP implementation, and provides a few required missing functions. Signed-off-by: David McKay Signed-off-by: Paul Mundt --- arch/sh/drivers/pci/pci.c | 4 ++++ arch/sh/include/asm/io.h | 10 ++++++++++ arch/sh/kernel/io.c | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 9a1c423ad167..c481df639022 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -295,6 +295,8 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, vma->vm_page_prot); } +#ifndef CONFIG_GENERIC_IOMAP + static void __iomem *ioport_map_pci(struct pci_dev *dev, unsigned long port, unsigned int nr) { @@ -346,6 +348,8 @@ void pci_iounmap(struct pci_dev *dev, void __iomem *addr) } EXPORT_SYMBOL(pci_iounmap); +#endif /* CONFIG_GENERIC_IOMAP */ + #ifdef CONFIG_HOTPLUG EXPORT_SYMBOL(pcibios_resource_to_bus); EXPORT_SYMBOL(pcibios_bus_to_resource); diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h index 25348141674b..aeecf432d48c 100644 --- a/arch/sh/include/asm/io.h +++ b/arch/sh/include/asm/io.h @@ -146,6 +146,7 @@ __BUILD_MEMORY_STRING(q, u64) #define readl_relaxed(a) readl(a) #define readq_relaxed(a) readq(a) +#ifndef CONFIG_GENERIC_IOMAP /* Simple MMIO */ #define ioread8(a) __raw_readb(a) #define ioread16(a) __raw_readw(a) @@ -166,6 +167,15 @@ __BUILD_MEMORY_STRING(q, u64) #define iowrite8_rep(a, s, c) __raw_writesb((a), (s), (c)) #define iowrite16_rep(a, s, c) __raw_writesw((a), (s), (c)) #define iowrite32_rep(a, s, c) __raw_writesl((a), (s), (c)) +#endif + +#define mmio_insb(p,d,c) __raw_readsb(p,d,c) +#define mmio_insw(p,d,c) __raw_readsw(p,d,c) +#define mmio_insl(p,d,c) __raw_readsl(p,d,c) + +#define mmio_outsb(p,s,c) __raw_writesb(p,s,c) +#define mmio_outsw(p,s,c) __raw_writesw(p,s,c) +#define mmio_outsl(p,s,c) __raw_writesl(p,s,c) /* synco on SH-4A, otherwise a nop */ #define mmiowb() wmb() diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c index 4f85fffaa557..d0ca9684b781 100644 --- a/arch/sh/kernel/io.c +++ b/arch/sh/kernel/io.c @@ -62,6 +62,8 @@ void memset_io(volatile void __iomem *dst, int c, unsigned long count) } EXPORT_SYMBOL(memset_io); +#ifndef CONFIG_GENERIC_IOMAP + void __iomem *ioport_map(unsigned long port, unsigned int nr) { void __iomem *ret; @@ -79,3 +81,5 @@ void ioport_unmap(void __iomem *addr) sh_mv.mv_ioport_unmap(addr); } EXPORT_SYMBOL(ioport_unmap); + +#endif /* CONFIG_GENERIC_IOMAP */ -- cgit v1.2.3 From 27a30f53bbb77fd7e2e0459197e23b63fe3b74d4 Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Mon, 24 Aug 2009 16:14:03 +0900 Subject: sh: kgdb: do not reload VBR while handling debugger breackpoint Save the VBR allowing GDB to dump full registers set but do not reload it as soon as the kgdb_handle_exception is invoked. Signed-off-by: Giuseppe Cavallaro Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/kernel/kgdb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index 305aad742aec..d29de7864f32 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -195,8 +195,6 @@ void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) regs->gbr = gdb_regs[GDB_GBR]; regs->mach = gdb_regs[GDB_MACH]; regs->macl = gdb_regs[GDB_MACL]; - - __asm__ __volatile__ ("ldc %0, vbr" : : "r" (gdb_regs[GDB_VBR])); } void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) -- cgit v1.2.3 From b46373e0d4b9f714ab757aae0c19c41fbcc73ef5 Mon Sep 17 00:00:00 2001 From: Jon Frosdick Date: Mon, 24 Aug 2009 16:20:44 +0900 Subject: sh: Use internal watchdog timer to perform reset This patches will trigger a reboot using the watchdog timer instead of double fault. Unlike the previous method, this one actually works in 32 bit mode. Reset should also be cleaner. Signed-off-by: Jon Frosdick Signed-off-by: Carl Shaw Signed-off-by: Paul Mundt --- arch/sh/kernel/process_32.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/sh/kernel/process_32.c b/arch/sh/kernel/process_32.c index 9fee977f176b..0673c4746be3 100644 --- a/arch/sh/kernel/process_32.c +++ b/arch/sh/kernel/process_32.c @@ -32,15 +32,35 @@ #include #include #include +#include int ubc_usercnt = 0; +#ifdef CONFIG_32BIT +static void watchdog_trigger_immediate(void) +{ + sh_wdt_write_cnt(0xFF); + sh_wdt_write_csr(0xC2); +} + +void machine_restart(char * __unused) +{ + local_irq_disable(); + + /* Use watchdog timer to trigger reset */ + watchdog_trigger_immediate(); + + while (1) + cpu_sleep(); +} +#else void machine_restart(char * __unused) { /* SR.BL=1 and invoke address error to let CPU reset (manual reset) */ asm volatile("ldc %0, sr\n\t" "mov.l @%1, %0" : : "r" (0x10000000), "r" (0x80000001)); } +#endif void machine_halt(void) { -- cgit v1.2.3 From d724a9c9d572e092d1ce820463f082697487b874 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Aug 2009 16:25:38 +0900 Subject: sh: Allow for kernel command line concatenation. So far kernel command line arguments could be passed in by a bootloader or defined as CONFIG_CMDLINE, which completely overwriting the first one. This change allows a developer to declare selected kernel parameters in a kernel configuration (eg. project-specific defconfig), retaining possibility of passing others by a bootloader. The obvious examples of the first type are MTD partition or bigphysarea-like region definitions, while "debug" option or network configuration should be given by a bootloader or a JTAG boot script. Signed-off-by: Pawel Moll Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/Kconfig | 27 +++++++++++++++++++++++---- arch/sh/kernel/setup.c | 6 +++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 80b4f9a743a1..2f5352c06a0e 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -767,12 +767,31 @@ config UBC_WAKEUP If unsure, say N. -config CMDLINE_BOOL - bool "Default bootloader kernel arguments" +choice + prompt "Kernel command line" + optional + default CMDLINE_OVERWRITE + help + Setting this option allows the kernel command line arguments + to be set. + +config CMDLINE_OVERWRITE + bool "Overwrite bootloader kernel arguments" + help + Given string will overwrite any arguments passed in by + a bootloader. + +config CMDLINE_EXTEND + bool "Extend bootloader kernel arguments" + help + Given string will be concatenated with arguments passed in + by a bootloader. + +endchoice config CMDLINE - string "Initial kernel command string" - depends on CMDLINE_BOOL + string "Kernel command line arguments string" + depends on CMDLINE_OVERWRITE || CMDLINE_EXTEND default "console=ttySC1,115200" endmenu diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 212e6bddaeb8..d13bbafb4e1b 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -404,10 +404,14 @@ void __init setup_arch(char **cmdline_p) if (!memory_end) memory_end = memory_start + __MEMORY_SIZE; -#ifdef CONFIG_CMDLINE_BOOL +#ifdef CONFIG_CMDLINE_OVERWRITE strlcpy(command_line, CONFIG_CMDLINE, sizeof(command_line)); #else strlcpy(command_line, COMMAND_LINE, sizeof(command_line)); +#ifdef CONFIG_CMDLINE_EXTEND + strlcat(command_line, " ", sizeof(command_line)); + strlcat(command_line, CONFIG_CMDLINE, sizeof(command_line)); +#endif #endif /* Save unparsed command line copy for /proc/cmdline */ -- cgit v1.2.3 From fea966f7564205fcf5919af9bde031e753419c96 Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 17:09:53 +0900 Subject: sh: Remove implicit sign extension from assembler immediates The SH instruction set has several instructions which accept an 8 bit immediate operand. For logical instructions this operand is zero extended, for arithmetic instructions the operand is sign extended. After adding an option to the assembler to check this, it was found that several pieces of assembly code were assuming this behaviour, and in one case getting it wrong. So this patch explicitly sign extends any immediate operands, which makes it obvious what is happening, and fixes the one case which got it wrong. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/boot/compressed/head_32.S | 2 +- arch/sh/include/asm/entry-macros.S | 2 +- arch/sh/kernel/cpu/sh3/entry.S | 2 +- arch/sh/kernel/entry-common.S | 5 +++-- arch/sh/lib/clear_page.S | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/sh/boot/compressed/head_32.S b/arch/sh/boot/compressed/head_32.S index 06ac31f3be88..02a30935f0b9 100644 --- a/arch/sh/boot/compressed/head_32.S +++ b/arch/sh/boot/compressed/head_32.S @@ -22,7 +22,7 @@ startup: bt clear_bss sub r0, r2 mov.l bss_start_addr, r0 - mov #0xe0, r1 + mov #0xffffffe0, r1 and r1, r0 ! align cache line mov.l text_start_addr, r3 mov r0, r1 diff --git a/arch/sh/include/asm/entry-macros.S b/arch/sh/include/asm/entry-macros.S index 64fd0de24daf..cc43a55e1fcf 100644 --- a/arch/sh/include/asm/entry-macros.S +++ b/arch/sh/include/asm/entry-macros.S @@ -7,7 +7,7 @@ .endm .macro sti - mov #0xf0, r11 + mov #0xfffffff0, r11 extu.b r11, r11 not r11, r11 stc sr, r10 diff --git a/arch/sh/kernel/cpu/sh3/entry.S b/arch/sh/kernel/cpu/sh3/entry.S index 8c19e21847d7..9421ec715fd2 100644 --- a/arch/sh/kernel/cpu/sh3/entry.S +++ b/arch/sh/kernel/cpu/sh3/entry.S @@ -257,7 +257,7 @@ restore_all: ! ! Calculate new SR value mov k3, k2 ! original SR value - mov #0xf0, k1 + mov #0xfffffff0, k1 extu.b k1, k1 not k1, k1 and k1, k2 ! Mask original SR value diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S index 700477601c6f..68d9223b145e 100644 --- a/arch/sh/kernel/entry-common.S +++ b/arch/sh/kernel/entry-common.S @@ -98,8 +98,9 @@ need_resched: mov #OFF_SR, r0 mov.l @(r0,r15), r0 ! get status register - and #0xf0, r0 ! interrupts off (exception path)? - cmp/eq #0xf0, r0 + shlr r0 + and #(0xf0>>1), r0 ! interrupts off (exception path)? + cmp/eq #(0xf0>>1), r0 bt noresched mov.l 3f, r0 jsr @r0 ! call preempt_schedule_irq diff --git a/arch/sh/lib/clear_page.S b/arch/sh/lib/clear_page.S index 8342bfbde64c..c92244d4ff9d 100644 --- a/arch/sh/lib/clear_page.S +++ b/arch/sh/lib/clear_page.S @@ -57,7 +57,7 @@ ENTRY(clear_page) ENTRY(__clear_user) ! mov #0, r0 - mov #0xe0, r1 ! 0xffffffe0 + mov #0xffffffe0, r1 ! ! r4..(r4+31)&~32 -------- not aligned [ Area 0 ] ! (r4+31)&~32..(r4+r5)&~32 -------- aligned [ Area 1 ] -- cgit v1.2.3 From 7d9c035150897c2bebedae280505513dbdef2abc Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 17:13:52 +0900 Subject: sh: Read from CCN_PVR instead of ROM for delay. Reading from the ROM is not a good idea as it could disturb some flash operation that it is in progress. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/include/asm/io.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h index aeecf432d48c..5be45ea4dfec 100644 --- a/arch/sh/include/asm/io.h +++ b/arch/sh/include/asm/io.h @@ -92,8 +92,12 @@ static inline void ctrl_delay(void) { -#ifdef P2SEG +#ifdef CONFIG_CPU_SH4 + __raw_readw(CCN_PVR); +#elif defined(P2SEG) __raw_readw(P2SEG); +#else +#error "Need a dummy address for delay" #endif } -- cgit v1.2.3 From 8af57f8b4c0ada9063b1cee9d81e3e59f04ce5a2 Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 17:26:39 +0900 Subject: sh: generic_in/outs{bwl} optimizations. After performing the port2addr conversion, and checking that the data is correctly aligned, simply call __raw_readsX/writesX. These have already been optimised. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/kernel/io_generic.c | 50 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c index 5a7f554d9ca1..4ff507239286 100644 --- a/arch/sh/kernel/io_generic.c +++ b/arch/sh/kernel/io_generic.c @@ -73,35 +73,19 @@ u32 generic_inl_p(unsigned long port) void generic_insb(unsigned long port, void *dst, unsigned long count) { - volatile u8 *port_addr; - u8 *buf = dst; - - port_addr = (volatile u8 __force *)__ioport_map(port, 1); - while (count--) - *buf++ = *port_addr; + __raw_readsb(__ioport_map(port, 1), dst, count); + dummy_read(); } void generic_insw(unsigned long port, void *dst, unsigned long count) { - volatile u16 *port_addr; - u16 *buf = dst; - - port_addr = (volatile u16 __force *)__ioport_map(port, 2); - while (count--) - *buf++ = *port_addr; - + __raw_readsw(__ioport_map(port, 2), dst, count); dummy_read(); } void generic_insl(unsigned long port, void *dst, unsigned long count) { - volatile u32 *port_addr; - u32 *buf = dst; - - port_addr = (volatile u32 __force *)__ioport_map(port, 4); - while (count--) - *buf++ = *port_addr; - + __raw_readsl(__ioport_map(port, 4), dst, count); dummy_read(); } @@ -145,37 +129,19 @@ void generic_outl_p(u32 b, unsigned long port) */ void generic_outsb(unsigned long port, const void *src, unsigned long count) { - volatile u8 *port_addr; - const u8 *buf = src; - - port_addr = (volatile u8 __force *)__ioport_map(port, 1); - - while (count--) - *port_addr = *buf++; + __raw_writesb(__ioport_map(port, 1), src, count); + dummy_read(); } void generic_outsw(unsigned long port, const void *src, unsigned long count) { - volatile u16 *port_addr; - const u16 *buf = src; - - port_addr = (volatile u16 __force *)__ioport_map(port, 2); - - while (count--) - *port_addr = *buf++; - + __raw_writesw(__ioport_map(port, 2), src, count); dummy_read(); } void generic_outsl(unsigned long port, const void *src, unsigned long count) { - volatile u32 *port_addr; - const u32 *buf = src; - - port_addr = (volatile u32 __force *)__ioport_map(port, 4); - while (count--) - *port_addr = *buf++; - + __raw_writesl(__ioport_map(port, 4), src, count); dummy_read(); } -- cgit v1.2.3 From 5e9377ec6f84e5334e9347e84e77d34e9a089ca7 Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 17:35:07 +0900 Subject: sh: Optimise memcpy_to/fromio for SH4 Optimise memcpy_to/fromio. This is used extensivly by MTD, so is a worthwhile performance gain. The main savings come from not repeatedly calling readl/writel, and doing word instead of byte at a time transfers. Also using "movca.l" on SH4 gives a small performance win. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/kernel/io.c | 93 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 21 deletions(-) diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c index d0ca9684b781..4770c241c679 100644 --- a/arch/sh/kernel/io.c +++ b/arch/sh/kernel/io.c @@ -1,12 +1,9 @@ /* - * linux/arch/sh/kernel/io.c + * arch/sh/kernel/io.c - Machine independent I/O functions. * - * Copyright (C) 2000 Stuart Menefy + * Copyright (C) 2000 - 2009 Stuart Menefy * Copyright (C) 2005 Paul Mundt * - * Provide real functions which expand to whatever the header file defined. - * Also definitions of machine independent IO functions. - * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. @@ -18,33 +15,87 @@ /* * Copy data from IO memory space to "real" memory space. - * This needs to be optimized. */ void memcpy_fromio(void *to, const volatile void __iomem *from, unsigned long count) { - unsigned char *p = to; - while (count) { - count--; - *p = readb(from); - p++; - from++; - } + /* + * Would it be worthwhile doing byte and long transfers first + * to try and get aligned? + */ +#ifdef CONFIG_CPU_SH4 + if ((count >= 0x20) && + (((u32)to & 0x1f) == 0) && (((u32)from & 0x3) == 0)) { + int tmp2, tmp3, tmp4, tmp5, tmp6; + + __asm__ __volatile__( + "1: \n\t" + "mov.l @%7+, r0 \n\t" + "mov.l @%7+, %2 \n\t" + "movca.l r0, @%0 \n\t" + "mov.l @%7+, %3 \n\t" + "mov.l @%7+, %4 \n\t" + "mov.l @%7+, %5 \n\t" + "mov.l @%7+, %6 \n\t" + "mov.l @%7+, r7 \n\t" + "mov.l @%7+, r0 \n\t" + "mov.l %2, @(0x04,%0) \n\t" + "mov #0x20, %2 \n\t" + "mov.l %3, @(0x08,%0) \n\t" + "sub %2, %1 \n\t" + "mov.l %4, @(0x0c,%0) \n\t" + "cmp/hi %1, %2 ! T if 32 > count \n\t" + "mov.l %5, @(0x10,%0) \n\t" + "mov.l %6, @(0x14,%0) \n\t" + "mov.l r7, @(0x18,%0) \n\t" + "mov.l r0, @(0x1c,%0) \n\t" + "bf.s 1b \n\t" + " add #0x20, %0 \n\t" + : "=&r" (to), "=&r" (count), + "=&r" (tmp2), "=&r" (tmp3), "=&r" (tmp4), + "=&r" (tmp5), "=&r" (tmp6), "=&r" (from) + : "7"(from), "0" (to), "1" (count) + : "r0", "r7", "t", "memory"); + } +#endif + + if ((((u32)to | (u32)from) & 0x3) == 0) { + for (; count > 3; count -= 4) { + *(u32 *)to = *(volatile u32 *)from; + to += 4; + from += 4; + } + } + + for (; count > 0; count--) { + *(u8 *)to = *(volatile u8 *)from; + to++; + from++; + } + + mb(); } EXPORT_SYMBOL(memcpy_fromio); /* * Copy data from "real" memory space to IO memory space. - * This needs to be optimized. */ void memcpy_toio(volatile void __iomem *to, const void *from, unsigned long count) { - const unsigned char *p = from; - while (count) { - count--; - writeb(*p, to); - p++; - to++; - } + if ((((u32)to | (u32)from) & 0x3) == 0) { + for ( ; count > 3; count -= 4) { + *(volatile u32 *)to = *(u32 *)from; + to += 4; + from += 4; + } + } + + for (; count > 0; count--) { + *(volatile u8 *)to = *(u8 *)from; + to++; + from++; + } + + mb(); } EXPORT_SYMBOL(memcpy_toio); -- cgit v1.2.3 From a5cf9e2444ec15de5407696ff21c32dd21ca0a8d Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 17:36:24 +0900 Subject: sh: Improve comments int SH4 cache flushing code This is a pure documentation, to try to explain why the cache flushing code for the SH4 is implemented the way it is. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/mm/cache-sh4.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 5cfe08dbb59e..7ce816188313 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c @@ -581,6 +581,17 @@ static void __flush_cache_4096(unsigned long addr, unsigned long phys, * Break the 1, 2 and 4 way variants of this out into separate functions to * avoid nearly all the overhead of having the conditional stuff in the function * bodies (+ the 1 and 2 way cases avoid saving any registers too). + * + * We want to eliminate unnecessary bus transactions, so this code uses + * a non-obvious technique. + * + * Loop over a cache way sized block of, one cache line at a time. For each + * line, use movca.a to cause the current cache line contents to be written + * back, but without reading anything from main memory. However this has the + * side effect that the cache is now caching that memory location. So follow + * this with a cache invalidate to mark the cache line invalid. And do all + * this with interrupts disabled, to avoid the cache line being accidently + * evicted while it is holding garbage. */ static void __flush_dcache_segment_1way(unsigned long start, unsigned long extent_per_way) -- cgit v1.2.3 From 6d243dd37002bcee54841852ab1b8606fd457851 Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 18:16:56 +0900 Subject: sh: Add sys_cacheflush() call for SH CPUs. Adds a system call to allow user code to flush code from the cache. You can use instructions for the data side, but the iside can only be done by a flush ROM which really only works with a direct mapped cache. The later SH4's have 2 way Iside, so this call allows a portable way to flush the cache. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/include/asm/Kbuild | 2 +- arch/sh/include/asm/cachectl.h | 12 ++++++++++++ arch/sh/include/asm/unistd_32.h | 2 +- arch/sh/include/asm/unistd_64.h | 2 +- arch/sh/kernel/sys_sh.c | 43 +++++++++++++++++++++++++++++++++++++++++ arch/sh/kernel/syscalls_32.S | 2 +- arch/sh/kernel/syscalls_64.S | 2 +- 7 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 arch/sh/include/asm/cachectl.h diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild index 43910cdf78a5..e121c30f797d 100644 --- a/arch/sh/include/asm/Kbuild +++ b/arch/sh/include/asm/Kbuild @@ -1,6 +1,6 @@ include include/asm-generic/Kbuild.asm -header-y += cpu-features.h +header-y += cachectl.h cpu-features.h unifdef-y += unistd_32.h unifdef-y += unistd_64.h diff --git a/arch/sh/include/asm/cachectl.h b/arch/sh/include/asm/cachectl.h new file mode 100644 index 000000000000..305dd7082a66 --- /dev/null +++ b/arch/sh/include/asm/cachectl.h @@ -0,0 +1,12 @@ +#ifndef _SH_CACHECTL_H +#define _SH_CACHECTL_H + +/* Definitions for the cacheflush system call. */ + +#define CACHEFLUSH_D_INVAL 0x1 /* invalidate (without write back) */ +#define CACHEFLUSH_D_WB 0x2 /* write back (without invalidate) */ +#define CACHEFLUSH_D_PURGE 0x3 /* writeback and invalidate */ + +#define CACHEFLUSH_I 0x4 + +#endif /* _SH_CACHECTL_H */ diff --git a/arch/sh/include/asm/unistd_32.h b/arch/sh/include/asm/unistd_32.h index 61d6ad93d786..925dd40d9d55 100644 --- a/arch/sh/include/asm/unistd_32.h +++ b/arch/sh/include/asm/unistd_32.h @@ -132,7 +132,7 @@ #define __NR_clone 120 #define __NR_setdomainname 121 #define __NR_uname 122 -#define __NR_modify_ldt 123 +#define __NR_cacheflush 123 #define __NR_adjtimex 124 #define __NR_mprotect 125 #define __NR_sigprocmask 126 diff --git a/arch/sh/include/asm/unistd_64.h b/arch/sh/include/asm/unistd_64.h index a751699afda3..2b84bc916bc5 100644 --- a/arch/sh/include/asm/unistd_64.h +++ b/arch/sh/include/asm/unistd_64.h @@ -137,7 +137,7 @@ #define __NR_clone 120 #define __NR_setdomainname 121 #define __NR_uname 122 -#define __NR_modify_ldt 123 +#define __NR_cacheflush 123 #define __NR_adjtimex 124 #define __NR_mprotect 125 #define __NR_sigprocmask 126 diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c index 90d00e47264d..ec65dd8842b1 100644 --- a/arch/sh/kernel/sys_sh.c +++ b/arch/sh/kernel/sys_sh.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include static inline long do_mmap2(unsigned long addr, unsigned long len, unsigned long prot, @@ -179,6 +181,47 @@ asmlinkage int sys_ipc(uint call, int first, int second, return -EINVAL; } +/* sys_cacheflush -- flush (part of) the processor cache. */ +asmlinkage int sys_cacheflush(unsigned long addr, unsigned long len, int op) +{ + struct vm_area_struct *vma; + + if ((op < 0) || (op > (CACHEFLUSH_D_PURGE|CACHEFLUSH_I))) + return -EINVAL; + + /* + * Verify that the specified address region actually belongs + * to this process. + */ + if (addr + len < addr) + return -EFAULT; + + down_read(¤t->mm->mmap_sem); + vma = find_vma (current->mm, addr); + if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end) { + up_read(¤t->mm->mmap_sem); + return -EFAULT; + } + + switch (op & CACHEFLUSH_D_PURGE) { + case CACHEFLUSH_D_INVAL: + __flush_invalidate_region((void *)addr, len); + break; + case CACHEFLUSH_D_WB: + __flush_wback_region((void *)addr, len); + break; + case CACHEFLUSH_D_PURGE: + __flush_purge_region((void *)addr, len); + break; + } + + if (op & CACHEFLUSH_I) + flush_cache_all(); + + up_read(¤t->mm->mmap_sem); + return 0; +} + asmlinkage int sys_uname(struct old_utsname __user *name) { int err; diff --git a/arch/sh/kernel/syscalls_32.S b/arch/sh/kernel/syscalls_32.S index f9e21fa2f592..16ba225ede89 100644 --- a/arch/sh/kernel/syscalls_32.S +++ b/arch/sh/kernel/syscalls_32.S @@ -139,7 +139,7 @@ ENTRY(sys_call_table) .long sys_clone /* 120 */ .long sys_setdomainname .long sys_newuname - .long sys_ni_syscall /* sys_modify_ldt */ + .long sys_cacheflush /* x86: sys_modify_ldt */ .long sys_adjtimex .long sys_mprotect /* 125 */ .long sys_sigprocmask diff --git a/arch/sh/kernel/syscalls_64.S b/arch/sh/kernel/syscalls_64.S index bf420b616ae0..af6fb7410c21 100644 --- a/arch/sh/kernel/syscalls_64.S +++ b/arch/sh/kernel/syscalls_64.S @@ -143,7 +143,7 @@ sys_call_table: .long sys_clone /* 120 */ .long sys_setdomainname .long sys_newuname - .long sys_ni_syscall /* sys_modify_ldt */ + .long sys_cacheflush /* x86: sys_modify_ldt */ .long sys_adjtimex .long sys_mprotect /* 125 */ .long sys_sigprocmask -- cgit v1.2.3 From bd4fb4d4c1e4a5a2ffbf57a83817a749df1339dd Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 18:18:50 +0900 Subject: sh: Fix underflow in SH udelay() code. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/lib/delay.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/sh/lib/delay.c b/arch/sh/lib/delay.c index f3ddd2133e6f..faa8f86c0db4 100644 --- a/arch/sh/lib/delay.c +++ b/arch/sh/lib/delay.c @@ -21,13 +21,14 @@ void __delay(unsigned long loops) inline void __const_udelay(unsigned long xloops) { + xloops *= 4; __asm__("dmulu.l %0, %2\n\t" "sts mach, %0" : "=r" (xloops) : "0" (xloops), - "r" (HZ * cpu_data[raw_smp_processor_id()].loops_per_jiffy) + "r" (cpu_data[raw_smp_processor_id()].loops_per_jiffy * (HZ/4)) : "macl", "mach"); - __delay(xloops); + __delay(++xloops); } void __udelay(unsigned long usecs) -- cgit v1.2.3 From 6000fc4d6f3e55ad52cce8d76317187fe01af2aa Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 18:27:33 +0900 Subject: sh: Fixes some write posting issues in the interrupt handling for SH It is possible for the CPU to re-enable it's interrupt block bit before the write to the interrupt controller has actually masked out the external interupt at the controller. We get around this by reading back from the interrupt controller which will ensure the write has happened. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/kernel/cpu/irq/ipr.c | 1 + drivers/sh/intc.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/sh/kernel/cpu/irq/ipr.c b/arch/sh/kernel/cpu/irq/ipr.c index 808d99a48efb..c1508a90fc6a 100644 --- a/arch/sh/kernel/cpu/irq/ipr.c +++ b/arch/sh/kernel/cpu/irq/ipr.c @@ -35,6 +35,7 @@ static void disable_ipr_irq(unsigned int irq) unsigned long addr = get_ipr_desc(irq)->ipr_offsets[p->ipr_idx]; /* Set the priority in IPR to 0 */ __raw_writew(__raw_readw(addr) & (0xffff ^ (0xf << p->shift)), addr); + (void)__raw_readw(addr); /* Read back to flush write posting */ } static void enable_ipr_irq(unsigned int irq) diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 3dd231a643b5..4b1ca9d28353 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -77,7 +77,7 @@ static unsigned long ack_handle[NR_IRQS]; static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { struct irq_chip *chip = get_irq_chip(irq); - return (void *)((char *)chip - offsetof(struct intc_desc_int, chip)); + return container_of(chip, struct intc_desc_int, chip); } static inline unsigned int set_field(unsigned int value, @@ -95,16 +95,19 @@ static inline unsigned int set_field(unsigned int value, static void write_8(unsigned long addr, unsigned long h, unsigned long data) { __raw_writeb(set_field(0, data, h), addr); + (void)__raw_readb(addr); /* Defeat write posting */ } static void write_16(unsigned long addr, unsigned long h, unsigned long data) { __raw_writew(set_field(0, data, h), addr); + (void)__raw_readw(addr); /* Defeat write posting */ } static void write_32(unsigned long addr, unsigned long h, unsigned long data) { __raw_writel(set_field(0, data, h), addr); + (void)__raw_readl(addr); /* Defeat write posting */ } static void modify_8(unsigned long addr, unsigned long h, unsigned long data) @@ -112,6 +115,7 @@ static void modify_8(unsigned long addr, unsigned long h, unsigned long data) unsigned long flags; local_irq_save(flags); __raw_writeb(set_field(__raw_readb(addr), data, h), addr); + (void)__raw_readb(addr); /* Defeat write posting */ local_irq_restore(flags); } @@ -120,6 +124,7 @@ static void modify_16(unsigned long addr, unsigned long h, unsigned long data) unsigned long flags; local_irq_save(flags); __raw_writew(set_field(__raw_readw(addr), data, h), addr); + (void)__raw_readw(addr); /* Defeat write posting */ local_irq_restore(flags); } @@ -128,6 +133,7 @@ static void modify_32(unsigned long addr, unsigned long h, unsigned long data) unsigned long flags; local_irq_save(flags); __raw_writel(set_field(__raw_readl(addr), data, h), addr); + (void)__raw_readl(addr); /* Defeat write posting */ local_irq_restore(flags); } -- cgit v1.2.3 From a1fce732359b80ead84efba23059a5f1b572b85a Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 18:29:25 +0900 Subject: sh: Fix overzealous checking in __ioremap() Allow peripherals before the start of RAM to be remapped. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/mm/ioremap_32.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/sh/mm/ioremap_32.c b/arch/sh/mm/ioremap_32.c index da2f4186f2cd..c3250614e3ae 100644 --- a/arch/sh/mm/ioremap_32.c +++ b/arch/sh/mm/ioremap_32.c @@ -57,14 +57,6 @@ void __iomem *__ioremap(unsigned long phys_addr, unsigned long size, if (is_pci_memory_fixed_range(phys_addr, size)) return (void __iomem *)phys_addr; -#if !defined(CONFIG_PMB_FIXED) - /* - * Don't allow anybody to remap normal RAM that we're using.. - */ - if (phys_addr < virt_to_phys(high_memory)) - return NULL; -#endif - /* * Mappings have to be page-aligned */ -- cgit v1.2.3 From ffad9d7a54a5e809007135595c778715aa0fb07a Mon Sep 17 00:00:00 2001 From: Stuart Menefy Date: Mon, 24 Aug 2009 18:39:39 +0900 Subject: sh: Fix problems with cache flushing when cache is in write-through mode Change the method used to flush the cache in write-through mode to avoid corrupted data being written back to memory. Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/mm/cache-sh4.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c index 7ce816188313..397c1030c7a6 100644 --- a/arch/sh/mm/cache-sh4.c +++ b/arch/sh/mm/cache-sh4.c @@ -592,6 +592,20 @@ static void __flush_cache_4096(unsigned long addr, unsigned long phys, * this with a cache invalidate to mark the cache line invalid. And do all * this with interrupts disabled, to avoid the cache line being accidently * evicted while it is holding garbage. + * + * This also breaks in a number of circumstances: + * - if there are modifications to the region of memory just above + * empty_zero_page (for example because a breakpoint has been placed + * there), then these can be lost. + * + * This is because the the memory address which the cache temporarily + * caches in the above description is empty_zero_page. So the + * movca.l hits the cache (it is assumed that it misses, or at least + * isn't dirty), modifies the line and then invalidates it, losing the + * required change. + * + * - If caches are disabled or configured in write-through mode, then + * the movca.l writes garbage directly into memory. */ static void __flush_dcache_segment_1way(unsigned long start, unsigned long extent_per_way) @@ -641,6 +655,25 @@ static void __flush_dcache_segment_1way(unsigned long start, } while (a0 < a0e); } +#ifdef CONFIG_CACHE_WRITETHROUGH +/* This method of cache flushing avoids the problems discussed + * in the comment above if writethrough caches are enabled. */ +static void __flush_dcache_segment_2way(unsigned long start, + unsigned long extent_per_way) +{ + unsigned long array_addr; + + array_addr = CACHE_OC_ADDRESS_ARRAY | + (start & cpu_data->dcache.entry_mask); + + while (extent_per_way) { + ctrl_outl(0, array_addr); + ctrl_outl(0, array_addr + cpu_data->dcache.way_incr); + array_addr += cpu_data->dcache.linesz; + extent_per_way -= cpu_data->dcache.linesz; + } +} +#else static void __flush_dcache_segment_2way(unsigned long start, unsigned long extent_per_way) { @@ -699,6 +732,7 @@ static void __flush_dcache_segment_2way(unsigned long start, a1 += linesz; } while (a0 < a0e); } +#endif static void __flush_dcache_segment_4way(unsigned long start, unsigned long extent_per_way) -- cgit v1.2.3 From 788e6af37a4ace8721eda72e4abe66fe0f6b49fd Mon Sep 17 00:00:00 2001 From: Giuseppe Cavallaro Date: Mon, 24 Aug 2009 18:59:09 +0900 Subject: sh: fix sys_cacheflush error checking sys_cacheflush should return with EINVAL if the cache parameter is not one of ICACHE, DCACHE or BCACHE. So, we need to include 0 in the first check. It also adds the three definitions above as wrapper of the existent macros. PS: ltp cacheflush01 test now passes. Signed-off-by: Giuseppe Cavallaro Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/include/asm/cachectl.h | 7 +++++++ arch/sh/kernel/sys_sh.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/sh/include/asm/cachectl.h b/arch/sh/include/asm/cachectl.h index 305dd7082a66..6ffb4b7a212e 100644 --- a/arch/sh/include/asm/cachectl.h +++ b/arch/sh/include/asm/cachectl.h @@ -9,4 +9,11 @@ #define CACHEFLUSH_I 0x4 +/* + * Options for cacheflush system call + */ +#define ICACHE CACHEFLUSH_I /* flush instruction cache */ +#define DCACHE CACHEFLUSH_D_PURGE /* writeback and flush data cache */ +#define BCACHE (ICACHE|DCACHE) /* flush both caches */ + #endif /* _SH_CACHECTL_H */ diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c index ec65dd8842b1..8aa5d1ceaf14 100644 --- a/arch/sh/kernel/sys_sh.c +++ b/arch/sh/kernel/sys_sh.c @@ -186,7 +186,7 @@ asmlinkage int sys_cacheflush(unsigned long addr, unsigned long len, int op) { struct vm_area_struct *vma; - if ((op < 0) || (op > (CACHEFLUSH_D_PURGE|CACHEFLUSH_I))) + if ((op <= 0) || (op > (CACHEFLUSH_D_PURGE|CACHEFLUSH_I))) return -EINVAL; /* -- cgit v1.2.3 From 05ecd5a1f76c183cca381705b3adb7d77c9a0439 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Aug 2009 19:52:38 +0900 Subject: sh: Simplify "multi-evt" interrupt handling. This patch changes the way in which "multi-evt" interrups are handled. The intc_evt2irq_table and related intc_evt2irq() have been removed and the "redirecting" handler is installed for the coupled interrupts. Thanks to that the do_IRQ() function don't have to use another level of indirection for all the interrupts... Signed-off-by: Pawel Moll Signed-off-by: Stuart Menefy Signed-off-by: Paul Mundt --- arch/sh/kernel/irq.c | 2 +- drivers/sh/intc.c | 54 ++++++++++++++++--------------------------------- include/linux/sh_intc.h | 1 - 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 278c68c60488..d1053392e287 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -114,7 +114,7 @@ asmlinkage int do_IRQ(unsigned int irq, struct pt_regs *regs) #endif irq_enter(); - irq = irq_demux(intc_evt2irq(irq)); + irq = irq_demux(evt2irq(irq)); #ifdef CONFIG_IRQSTACKS curctx = (union irq_ctx *)current_thread_info(); diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 4b1ca9d28353..a9174ec72853 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -663,16 +663,9 @@ static unsigned int __init save_reg(struct intc_desc_int *d, return 0; } -static unsigned char *intc_evt2irq_table; - -unsigned int intc_evt2irq(unsigned int vector) +static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) { - unsigned int irq = evt2irq(vector); - - if (intc_evt2irq_table && intc_evt2irq_table[irq]) - irq = intc_evt2irq_table[irq]; - - return irq; + generic_handle_irq((unsigned int)get_irq_data(irq)); } void __init register_intc_controller(struct intc_desc *desc) @@ -745,34 +738,6 @@ void __init register_intc_controller(struct intc_desc *desc) BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ - /* keep the first vector only if same enum is used multiple times */ - for (i = 0; i < desc->nr_vectors; i++) { - struct intc_vect *vect = desc->vectors + i; - int first_irq = evt2irq(vect->vect); - - if (!vect->enum_id) - continue; - - for (k = i + 1; k < desc->nr_vectors; k++) { - struct intc_vect *vect2 = desc->vectors + k; - - if (vect->enum_id != vect2->enum_id) - continue; - - vect2->enum_id = 0; - - if (!intc_evt2irq_table) - intc_evt2irq_table = kzalloc(NR_IRQS, GFP_NOWAIT); - - if (!intc_evt2irq_table) { - pr_warning("intc: cannot allocate evt2irq!\n"); - continue; - } - - intc_evt2irq_table[evt2irq(vect2->vect)] = first_irq; - } - } - /* register the vectors one by one */ for (i = 0; i < desc->nr_vectors; i++) { struct intc_vect *vect = desc->vectors + i; @@ -789,6 +754,21 @@ void __init register_intc_controller(struct intc_desc *desc) } intc_register_irq(desc, d, vect->enum_id, irq); + + for (k = i + 1; k < desc->nr_vectors; k++) { + struct intc_vect *vect2 = desc->vectors + k; + unsigned int irq2 = evt2irq(vect2->vect); + + if (vect->enum_id != vect2->enum_id) + continue; + + vect2->enum_id = 0; + + /* redirect this interrupts to the first one */ + set_irq_chip_and_handler_name(irq2, &d->chip, + intc_redirect_irq, "redirect"); + set_irq_data(irq2, (void *)irq); + } } } diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index eb1423a0078d..68e212ff9dde 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h @@ -85,7 +85,6 @@ struct intc_desc symbol __initdata = { \ } #endif -unsigned int intc_evt2irq(unsigned int vector); void __init register_intc_controller(struct intc_desc *desc); int intc_set_priority(unsigned int irq, unsigned int prio); -- cgit v1.2.3